Implement core physics and pack validation
This commit is contained in:
parent
fbbd7c52e3
commit
6bad4189d2
43 changed files with 1365 additions and 686 deletions
|
|
@ -8,6 +8,9 @@ import numpy as np
|
|||
|
||||
from algebra.versor import versor_condition
|
||||
from core.config import DEFAULT_CONFIG, RuntimeConfig
|
||||
from core.physics.drive import GradientField, ValueAxis
|
||||
from core.physics.exertion import CycleCost, ExertionMeter
|
||||
from core.physics.identity import IdentityManifold
|
||||
from generate.articulation import ArticulationPlan, realize
|
||||
from generate.dialogue import DialogueRole, classify_dialogue_blade, propose_dialogue
|
||||
from generate.proposition import FrameRegistry, Proposition, propose
|
||||
|
|
@ -94,6 +97,12 @@ class ChatRuntime:
|
|||
self._pos_by_surface = {
|
||||
e.surface: (e.pos or e.part_of_speech or "X") for e in entries
|
||||
}
|
||||
self.identity_manifold = _default_identity_manifold()
|
||||
self.exertion_meter = ExertionMeter(capacity_ceiling=128.0)
|
||||
self.drive_gradients = tuple(
|
||||
GradientField(axis=axis, magnitude=0.75)
|
||||
for axis in self.identity_manifold.value_axes
|
||||
)
|
||||
|
||||
@property
|
||||
def session(self) -> SessionContext:
|
||||
|
|
@ -190,6 +199,15 @@ class ChatRuntime:
|
|||
salience_top_k=self.config.salience_top_k,
|
||||
inhibition_threshold=self.config.inhibition_threshold,
|
||||
)
|
||||
self.exertion_meter.record(
|
||||
CycleCost(
|
||||
cycle_index=self._context.turn,
|
||||
attention_cost=float(result.candidates_used or 0),
|
||||
inhibition_cost=float(self.config.inhibition_threshold),
|
||||
digest_cost=0.0,
|
||||
trajectory_cost=float(len(result.trajectory or ())),
|
||||
)
|
||||
)
|
||||
self._context.state = result.final_state
|
||||
self._context.vault.store(
|
||||
result.final_state.F,
|
||||
|
|
@ -216,3 +234,31 @@ class ChatRuntime:
|
|||
return self.chat(text, max_tokens=max_tokens).surface
|
||||
except ValueError:
|
||||
return ""
|
||||
|
||||
|
||||
def _default_identity_manifold() -> IdentityManifold:
|
||||
axes = (
|
||||
ValueAxis(
|
||||
axis_id="truthfulness",
|
||||
name="truthfulness",
|
||||
direction=(1.0, 0.0, 0.0),
|
||||
theological_note="Truth is treated as a fixed value axis, not a prompt preference.",
|
||||
),
|
||||
ValueAxis(
|
||||
axis_id="coherence",
|
||||
name="coherence",
|
||||
direction=(0.0, 1.0, 0.0),
|
||||
theological_note="Operations must preserve field coherence under propagation.",
|
||||
),
|
||||
ValueAxis(
|
||||
axis_id="reverence",
|
||||
name="reverence",
|
||||
direction=(0.0, 0.0, 1.0),
|
||||
theological_note="Depth-language handling remains bounded by source structure.",
|
||||
),
|
||||
)
|
||||
return IdentityManifold(
|
||||
value_axes=axes,
|
||||
boundary_ids=frozenset({"no_fabricated_source", "no_hot_path_repair"}),
|
||||
alignment_threshold=0.75,
|
||||
)
|
||||
|
|
|
|||
34
core/cli.py
34
core/cli.py
|
|
@ -95,7 +95,10 @@ def cmd_chat(args: argparse.Namespace) -> int:
|
|||
def cmd_test(args: argparse.Namespace) -> int:
|
||||
"""Run pytest. Extra args are forwarded unchanged."""
|
||||
default_args = ["-q", "--tb=short"]
|
||||
return _run(sys.executable, "-m", "pytest", *(args.args or default_args))
|
||||
forwarded = list(args.args or default_args)
|
||||
if forwarded and forwarded[0] == "--":
|
||||
forwarded = forwarded[1:]
|
||||
return _run(sys.executable, "-m", "pytest", *forwarded)
|
||||
|
||||
|
||||
def cmd_check(args: argparse.Namespace) -> int:
|
||||
|
|
@ -276,6 +279,31 @@ def cmd_pack_verify(args: argparse.Namespace) -> int:
|
|||
return _run(sys.executable, "-m", "language_packs", "verify", args.pack_id)
|
||||
|
||||
|
||||
def cmd_pack_validate(args: argparse.Namespace) -> int:
|
||||
"""Run executable source-pack validation gates."""
|
||||
import importlib.util
|
||||
|
||||
pack_dir = _REPO_ROOT / "packs" / args.pack_id
|
||||
validator_path = pack_dir / "validators.py"
|
||||
if not validator_path.exists():
|
||||
_die(f"source-pack validator not found: {validator_path}", code=1)
|
||||
spec = importlib.util.spec_from_file_location(f"{args.pack_id}_validators", validator_path)
|
||||
if spec is None or spec.loader is None:
|
||||
_die(f"cannot load source-pack validator: {validator_path}", code=1)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
report = module.validate_pack()
|
||||
if args.json:
|
||||
print(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True))
|
||||
else:
|
||||
print(f"pack_id: {report['pack_id']}")
|
||||
print(f"active : {report['active']}")
|
||||
for name, result in report["gates"].items():
|
||||
status = "PASS" if result["passed"] else "FAIL"
|
||||
print(f"{status} {name:<12} {result['reason']}")
|
||||
return 0 if report["active"] else 1
|
||||
|
||||
|
||||
def _print_rust_status() -> bool:
|
||||
from algebra.backend import using_rust
|
||||
|
||||
|
|
@ -434,6 +462,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
pack_verify = pack_sub.add_parser("verify", help="verify a pack checksum")
|
||||
pack_verify.add_argument("pack_id", help="pack id, e.g. en_minimal_v1")
|
||||
pack_verify.set_defaults(func=cmd_pack_verify)
|
||||
pack_validate = pack_sub.add_parser("validate", help="validate a source pack under packs/")
|
||||
pack_validate.add_argument("pack_id", help="source pack id, e.g. en, he, grc, el")
|
||||
pack_validate.add_argument("--json", action="store_true", help="emit machine-readable JSON")
|
||||
pack_validate.set_defaults(func=cmd_pack_validate)
|
||||
|
||||
rust = subparsers.add_parser(
|
||||
"rust",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ State lives in the FieldState; operators are pure transformations.
|
|||
"""
|
||||
|
||||
from core.physics.salience import SalienceOperator, SalienceMap, FieldRegion
|
||||
from core.physics.energy import EnergyClass, EnergyProfile, FieldEnergyOperator
|
||||
from core.physics.valence import ValenceBundle
|
||||
from core.physics.attention import AttentionOperator, AttentionPlan, CoherenceBudget
|
||||
from core.physics.inhibition import InhibitionOperator, InhibitionMask
|
||||
from core.physics.binding import BindingFrame, BindingOperator
|
||||
|
|
@ -19,9 +21,11 @@ from core.physics.articulation import ArticulationPlan, ArticulationPlanner, Out
|
|||
from core.physics.drive import DriveGradientMap, GradientField, ValueAxis
|
||||
from core.physics.exertion import ExertionMeter, FatigueIndex, CycleCost
|
||||
from core.physics.identity import IdentityManifold, IdentityCheck, IdentityScore, CharacterProfile
|
||||
from core.physics.learning import PromotionDecision, VaultPromotionPolicy
|
||||
|
||||
__all__ = [
|
||||
"SalienceOperator", "SalienceMap", "FieldRegion",
|
||||
"EnergyClass", "EnergyProfile", "FieldEnergyOperator", "ValenceBundle",
|
||||
"AttentionOperator", "AttentionPlan", "CoherenceBudget",
|
||||
"InhibitionOperator", "InhibitionMask",
|
||||
"BindingFrame", "BindingOperator",
|
||||
|
|
@ -31,4 +35,5 @@ __all__ = [
|
|||
"DriveGradientMap", "GradientField", "ValueAxis",
|
||||
"ExertionMeter", "FatigueIndex", "CycleCost",
|
||||
"IdentityManifold", "IdentityCheck", "IdentityScore", "CharacterProfile",
|
||||
"PromotionDecision", "VaultPromotionPolicy",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ Each output segment carries full field provenance.
|
|||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
import hashlib
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, auto
|
||||
from typing import Tuple
|
||||
|
|
@ -51,4 +52,55 @@ class ArticulationPlanner:
|
|||
trajectory,
|
||||
modality: OutputModality,
|
||||
) -> ArticulationPlan:
|
||||
raise NotImplementedError("ArticulationPlanner.plan: implement plan construction")
|
||||
segments: list[ArticulationSegment] = []
|
||||
for idx, frame in enumerate(trajectory.frames):
|
||||
confidence = max(0.0, min(1.0, float(frame.coherence_magnitude)))
|
||||
source_regions = tuple(sorted(str(region_id) for region_id in frame.region_ids))
|
||||
segment_id = _segment_id(trajectory.trajectory_id, frame.frame_id, idx)
|
||||
segments.append(
|
||||
ArticulationSegment(
|
||||
segment_id=segment_id,
|
||||
source_frame_id=frame.frame_id,
|
||||
source_region_ids=source_regions,
|
||||
confidence=confidence,
|
||||
modality=modality,
|
||||
formatting_constraints=_constraints_for(modality),
|
||||
)
|
||||
)
|
||||
overall = (
|
||||
sum(segment.confidence for segment in segments) / len(segments)
|
||||
if segments
|
||||
else 0.0
|
||||
)
|
||||
return ArticulationPlan(
|
||||
plan_id=_plan_id(trajectory.trajectory_id, modality, tuple(segments)),
|
||||
segments=tuple(segments),
|
||||
source_trajectory_id=trajectory.trajectory_id,
|
||||
target_modality=modality,
|
||||
overall_confidence=overall,
|
||||
)
|
||||
|
||||
|
||||
def _constraints_for(modality: OutputModality) -> Tuple[str, ...]:
|
||||
if modality is OutputModality.CODE:
|
||||
return ("preserve_syntax", "monospace")
|
||||
if modality is OutputModality.STRUCTURED_DATA:
|
||||
return ("machine_readable", "schema_stable")
|
||||
if modality is OutputModality.HEBREW:
|
||||
return ("rtl", "preserve_script")
|
||||
if modality is OutputModality.KOINE_GREEK:
|
||||
return ("polytonic", "preserve_script")
|
||||
return ("plain_text",)
|
||||
|
||||
|
||||
def _segment_id(trajectory_id: str, frame_id: str, idx: int) -> str:
|
||||
return hashlib.sha256(f"{trajectory_id}:{frame_id}:{idx}".encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def _plan_id(trajectory_id: str, modality: OutputModality, segments: Tuple[ArticulationSegment, ...]) -> str:
|
||||
h = hashlib.sha256()
|
||||
h.update(trajectory_id.encode("utf-8"))
|
||||
h.update(modality.name.encode("ascii"))
|
||||
for segment in segments:
|
||||
h.update(segment.segment_id.encode("utf-8"))
|
||||
return h.hexdigest()
|
||||
|
|
|
|||
|
|
@ -48,4 +48,27 @@ class AttentionOperator:
|
|||
"""Produces an AttentionPlan from a SalienceMap and CoherenceBudget."""
|
||||
|
||||
def plan(self, salience_map, budget: CoherenceBudget, cycle_index: int) -> AttentionPlan:
|
||||
raise NotImplementedError("AttentionOperator.plan: implement traversal scheduling")
|
||||
steps: list[TraversalStep] = []
|
||||
spent = 0.0
|
||||
max_curvature = max(
|
||||
(float(entry.curvature_magnitude) for entry in salience_map.entries),
|
||||
default=0.0,
|
||||
)
|
||||
if max_curvature <= 0.0:
|
||||
return AttentionPlan(steps=(), total_cost=0.0, cycle_index=cycle_index)
|
||||
for entry in salience_map.entries:
|
||||
depth = max(0.0, min(1.0, float(entry.curvature_magnitude) / max_curvature))
|
||||
duration = max(1.0, float(entry.influence_radius))
|
||||
cost = depth * duration
|
||||
if spent + cost > budget.available:
|
||||
break
|
||||
steps.append(
|
||||
TraversalStep(
|
||||
region_id=entry.region_id,
|
||||
depth=depth,
|
||||
duration=duration,
|
||||
cost=cost,
|
||||
)
|
||||
)
|
||||
spent += cost
|
||||
return AttentionPlan(steps=tuple(steps), total_cost=spent, cycle_index=cycle_index)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ by coherence threshold, not by clock tick.
|
|||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
import hashlib
|
||||
from dataclasses import dataclass
|
||||
from typing import FrozenSet
|
||||
|
||||
|
|
@ -34,4 +35,45 @@ class BindingOperator:
|
|||
coherence_threshold: float,
|
||||
cycle_index: int,
|
||||
) -> BindingFrame | None:
|
||||
raise NotImplementedError("BindingOperator.bind: implement co-activation fusion")
|
||||
region_ids = _region_ids(attention_plan)
|
||||
if not region_ids:
|
||||
return None
|
||||
coherence = _coherence(attention_plan, field_state)
|
||||
if coherence < coherence_threshold:
|
||||
return None
|
||||
ordered = tuple(sorted(region_ids))
|
||||
frame_id = _hash_parts(("frame", str(cycle_index), *ordered))
|
||||
content_address = _hash_parts((frame_id, f"{coherence:.12f}", *ordered))
|
||||
return BindingFrame(
|
||||
frame_id=frame_id,
|
||||
region_ids=frozenset(ordered),
|
||||
coherence_magnitude=coherence,
|
||||
cycle_index=cycle_index,
|
||||
content_address=content_address,
|
||||
)
|
||||
|
||||
|
||||
def _region_ids(attention_plan) -> frozenset[str]:
|
||||
if hasattr(attention_plan, "steps"):
|
||||
return frozenset(str(step.region_id) for step in attention_plan.steps)
|
||||
if hasattr(attention_plan, "allowed_indices"):
|
||||
return frozenset(str(int(idx)) for idx in attention_plan.allowed_indices)
|
||||
return frozenset()
|
||||
|
||||
|
||||
def _coherence(attention_plan, field_state) -> float:
|
||||
if hasattr(attention_plan, "steps") and attention_plan.steps:
|
||||
depths = [float(step.depth) for step in attention_plan.steps]
|
||||
return max(0.0, min(1.0, sum(depths) / len(depths)))
|
||||
energy = getattr(field_state, "energy", None)
|
||||
if energy is not None:
|
||||
return max(0.0, min(1.0, float(energy.raw)))
|
||||
return 1.0 if _region_ids(attention_plan) else 0.0
|
||||
|
||||
|
||||
def _hash_parts(parts: tuple[str, ...]) -> str:
|
||||
h = hashlib.sha256()
|
||||
for part in parts:
|
||||
h.update(part.encode("utf-8"))
|
||||
h.update(b"\0")
|
||||
return h.hexdigest()
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ regions. It propagates a coherence wave outward from the binding frame.
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
|
||||
from field.state import FieldState
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DigestCycle:
|
||||
|
|
@ -28,6 +32,25 @@ class DigestOperator:
|
|||
|
||||
def digest(self, binding_frame, field_state, budget_reserve: float) -> tuple:
|
||||
"""Returns (updated_field_state, DigestCycle)."""
|
||||
raise NotImplementedError(
|
||||
"DigestOperator.digest: implement coherence wave propagation"
|
||||
coherence = max(0.0, min(1.0, float(binding_frame.coherence_magnitude)))
|
||||
budget = max(0.0, float(budget_reserve))
|
||||
consumed = min(budget, coherence * max(1, len(binding_frame.region_ids)))
|
||||
radius = consumed / max(1, len(binding_frame.region_ids))
|
||||
updated = FieldState(
|
||||
F=np.asarray(field_state.F).copy(),
|
||||
node=field_state.node,
|
||||
step=field_state.step + 1,
|
||||
holonomy=field_state.holonomy,
|
||||
energy=getattr(field_state, "energy", None),
|
||||
valence=getattr(field_state, "valence", None),
|
||||
)
|
||||
return (
|
||||
updated,
|
||||
DigestCycle(
|
||||
frame_id=binding_frame.frame_id,
|
||||
propagation_radius=radius,
|
||||
coherence_delta=coherence * radius,
|
||||
cycle_index=binding_frame.cycle_index,
|
||||
budget_consumed=consumed,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ into a combined gradient landscape.
|
|||
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Tuple
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -35,4 +35,12 @@ class DriveGradientMap:
|
|||
|
||||
def combined_bias(self, coordinates: Tuple[float, ...]) -> Tuple[float, ...]:
|
||||
"""Return the additive gradient bias vector at the given field coordinates."""
|
||||
raise NotImplementedError("DriveGradientMap.combined_bias: implement gradient composition")
|
||||
if not coordinates:
|
||||
return ()
|
||||
bias = [0.0 for _ in coordinates]
|
||||
for gradient in self.gradients:
|
||||
if not gradient.active:
|
||||
continue
|
||||
for idx, component in enumerate(gradient.axis.direction[: len(bias)]):
|
||||
bias[idx] += float(component) * float(gradient.magnitude)
|
||||
return tuple(bias)
|
||||
|
|
|
|||
118
core/physics/energy.py
Normal file
118
core/physics/energy.py
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
"""core.physics.energy — ADR-0006 scalar companion classes.
|
||||
|
||||
The operator assigns a bounded thermodynamic class from structural inputs:
|
||||
convergence density, recent activation, coherence residual, and morphology
|
||||
aspect. It does not inspect grades, repair fields, or normalize anything.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from math import exp, log1p
|
||||
from typing import Mapping
|
||||
|
||||
|
||||
class EnergyClass(str, Enum):
|
||||
E0 = "E0"
|
||||
E1 = "E1"
|
||||
E2 = "E2"
|
||||
E3 = "E3"
|
||||
E4 = "E4"
|
||||
|
||||
@property
|
||||
def vault_candidate(self) -> bool:
|
||||
return self in {EnergyClass.E0, EnergyClass.E1}
|
||||
|
||||
@property
|
||||
def governance_critical(self) -> bool:
|
||||
return self is EnergyClass.E4
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class EnergyProfile:
|
||||
raw: float
|
||||
energy_class: EnergyClass
|
||||
convergence_density: int = 0
|
||||
activation_count: int = 0
|
||||
last_activation_cycle: int = 0
|
||||
coherence_residual: float = 0.0
|
||||
aspect_weight: float = 0.0
|
||||
anchor_adjacent: bool = False
|
||||
|
||||
@property
|
||||
def requires_architect_review(self) -> bool:
|
||||
return self.energy_class.governance_critical or (
|
||||
self.anchor_adjacent and self.energy_class in {EnergyClass.E3, EnergyClass.E4}
|
||||
)
|
||||
|
||||
|
||||
_ASPECT_WEIGHTS: dict[str, float] = {
|
||||
"qatal": 0.15,
|
||||
"aorist": 0.15,
|
||||
"wayyiqtol": 0.45,
|
||||
"perfect": 0.25,
|
||||
"yiqtol": 0.65,
|
||||
"imperfect": 0.70,
|
||||
"present": 0.65,
|
||||
"cohortative": 0.55,
|
||||
"optative": 0.50,
|
||||
"imperative": 0.90,
|
||||
"jussive": 0.60,
|
||||
"subjunctive": 0.55,
|
||||
}
|
||||
|
||||
|
||||
def aspect_weight(features: Mapping[str, object] | None) -> float:
|
||||
if not features:
|
||||
return 0.0
|
||||
values = [
|
||||
str(value).lower()
|
||||
for key, value in features.items()
|
||||
if key in {"aspect", "tense", "mood"}
|
||||
]
|
||||
return max((_ASPECT_WEIGHTS.get(value, 0.0) for value in values), default=0.0)
|
||||
|
||||
|
||||
class FieldEnergyOperator:
|
||||
"""Compute ADR-0006 energy class from explicit structural inputs."""
|
||||
|
||||
def compute(
|
||||
self,
|
||||
*,
|
||||
convergence_density: int = 0,
|
||||
activation_count: int = 0,
|
||||
current_cycle: int = 0,
|
||||
last_activation_cycle: int = 0,
|
||||
coherence_residual: float = 0.0,
|
||||
morphology_features: Mapping[str, object] | None = None,
|
||||
anchor_adjacent: bool = False,
|
||||
) -> EnergyProfile:
|
||||
convergence = min(log1p(max(0, convergence_density)) / log1p(8), 1.0)
|
||||
age = max(0, int(current_cycle) - int(last_activation_cycle))
|
||||
recency = min(max(0, activation_count), 8) / 8.0 * exp(-age / 12.0)
|
||||
residual = min(max(0.0, float(coherence_residual)), 1.0)
|
||||
aspect = aspect_weight(morphology_features)
|
||||
raw = (0.35 * convergence) + (0.25 * recency) + (0.20 * residual) + (0.20 * aspect)
|
||||
if anchor_adjacent and raw >= 0.72:
|
||||
energy_class = EnergyClass.E4
|
||||
elif raw >= 0.82:
|
||||
energy_class = EnergyClass.E4
|
||||
elif raw >= 0.62:
|
||||
energy_class = EnergyClass.E3
|
||||
elif raw >= 0.38:
|
||||
energy_class = EnergyClass.E2
|
||||
elif raw >= 0.16:
|
||||
energy_class = EnergyClass.E1
|
||||
else:
|
||||
energy_class = EnergyClass.E0
|
||||
return EnergyProfile(
|
||||
raw=raw,
|
||||
energy_class=energy_class,
|
||||
convergence_density=max(0, convergence_density),
|
||||
activation_count=max(0, activation_count),
|
||||
last_activation_cycle=max(0, last_activation_cycle),
|
||||
coherence_residual=residual,
|
||||
aspect_weight=aspect,
|
||||
anchor_adjacent=anchor_adjacent,
|
||||
)
|
||||
|
|
@ -43,7 +43,28 @@ class IdentityCheck:
|
|||
"""Checks a ReasoningTrajectory against an IdentityManifold."""
|
||||
|
||||
def check(self, trajectory, manifold: IdentityManifold) -> IdentityScore:
|
||||
raise NotImplementedError("IdentityCheck.check: implement manifold alignment check")
|
||||
if not manifold.value_axes:
|
||||
return IdentityScore(
|
||||
score=1.0,
|
||||
flagged=False,
|
||||
deviation_axes=frozenset(),
|
||||
trajectory_id=trajectory.trajectory_id,
|
||||
)
|
||||
confidence = getattr(trajectory, "total_coherence_delta", 0.0)
|
||||
if trajectory.frames:
|
||||
confidence += sum(float(frame.coherence_magnitude) for frame in trajectory.frames) / len(trajectory.frames)
|
||||
score = max(0.0, min(1.0, 0.5 + (confidence / 2.0)))
|
||||
deviations = frozenset(
|
||||
axis.axis_id
|
||||
for axis in manifold.value_axes
|
||||
if score < manifold.alignment_threshold
|
||||
)
|
||||
return IdentityScore(
|
||||
score=score,
|
||||
flagged=score < manifold.alignment_threshold,
|
||||
deviation_axes=deviations,
|
||||
trajectory_id=trajectory.trajectory_id,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,27 @@ class InhibitionOperator:
|
|||
"""
|
||||
|
||||
def mask(self, attention_plan, field_state, cycle_index: int) -> InhibitionMask:
|
||||
raise NotImplementedError(
|
||||
"InhibitionOperator.mask: implement interference suppression"
|
||||
active = _active_regions(attention_plan)
|
||||
suppressed = _candidate_regions(field_state) - active
|
||||
coherence_delta = min(1.0, 0.05 * len(suppressed))
|
||||
return InhibitionMask(
|
||||
suppressed_region_ids=frozenset(sorted(suppressed)),
|
||||
suppression_reason="outside_attention_plan",
|
||||
coherence_delta=coherence_delta,
|
||||
cycle_index=cycle_index,
|
||||
)
|
||||
|
||||
|
||||
def _active_regions(attention_plan) -> set[str]:
|
||||
if hasattr(attention_plan, "steps"):
|
||||
return {str(step.region_id) for step in attention_plan.steps}
|
||||
if hasattr(attention_plan, "allowed_indices"):
|
||||
return {str(int(idx)) for idx in attention_plan.allowed_indices}
|
||||
return set()
|
||||
|
||||
|
||||
def _candidate_regions(field_state) -> set[str]:
|
||||
candidates = getattr(field_state, "candidate_region_ids", None)
|
||||
if candidates is None:
|
||||
return set()
|
||||
return {str(region_id) for region_id in candidates}
|
||||
|
|
|
|||
32
core/physics/learning.py
Normal file
32
core/physics/learning.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
"""core.physics.learning — ADR-0014 vault promotion criteria."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from core.physics.energy import EnergyClass, EnergyProfile
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class PromotionDecision:
|
||||
promote: bool
|
||||
reason: str
|
||||
energy_class: EnergyClass
|
||||
|
||||
|
||||
class VaultPromotionPolicy:
|
||||
"""Promote only settled, coherent regions into deep vault storage."""
|
||||
|
||||
def __init__(self, residual_threshold: float = 0.05) -> None:
|
||||
if residual_threshold < 0.0:
|
||||
raise ValueError("residual_threshold must be non-negative")
|
||||
self.residual_threshold = float(residual_threshold)
|
||||
|
||||
def decide(self, energy: EnergyProfile | None) -> PromotionDecision:
|
||||
if energy is None:
|
||||
return PromotionDecision(False, "missing_energy_profile", EnergyClass.E2)
|
||||
if not energy.energy_class.vault_candidate:
|
||||
return PromotionDecision(False, "region_still_active", energy.energy_class)
|
||||
if energy.coherence_residual > self.residual_threshold:
|
||||
return PromotionDecision(False, "coherence_residual_above_threshold", energy.energy_class)
|
||||
return PromotionDecision(True, "settled_coherent_region", energy.energy_class)
|
||||
|
|
@ -7,7 +7,8 @@ Trajectories are append-only; no in-place mutation after construction.
|
|||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
import hashlib
|
||||
from dataclasses import dataclass
|
||||
from typing import FrozenSet, Tuple
|
||||
|
||||
|
||||
|
|
@ -37,6 +38,42 @@ class TrajectoryOperator:
|
|||
"""Builds a ReasoningTrajectory from an ordered sequence of BindingFrames."""
|
||||
|
||||
def build(self, frames: list, trajectory_id: str) -> ReasoningTrajectory:
|
||||
raise NotImplementedError(
|
||||
"TrajectoryOperator.build: implement trajectory construction"
|
||||
ordered = tuple(frames)
|
||||
transitions: list[TrajectoryTransition] = []
|
||||
for left, right in zip(ordered, ordered[1:]):
|
||||
left_regions = set(left.region_ids)
|
||||
right_regions = set(right.region_ids)
|
||||
spine = frozenset(left_regions & right_regions)
|
||||
diff = frozenset(left_regions ^ right_regions)
|
||||
delta = float(right.coherence_magnitude) - float(left.coherence_magnitude)
|
||||
transitions.append(
|
||||
TrajectoryTransition(
|
||||
from_frame_id=left.frame_id,
|
||||
to_frame_id=right.frame_id,
|
||||
pressure_delta=delta,
|
||||
continuity_spine=spine,
|
||||
differential_set=diff,
|
||||
coherence_won=max(0.0, delta),
|
||||
coherence_lost=max(0.0, -delta),
|
||||
)
|
||||
)
|
||||
total = sum(t.pressure_delta for t in transitions)
|
||||
if ordered:
|
||||
span = (ordered[0].cycle_index, ordered[-1].cycle_index)
|
||||
else:
|
||||
span = (0, 0)
|
||||
resolved_id = trajectory_id or _trajectory_id(ordered)
|
||||
return ReasoningTrajectory(
|
||||
trajectory_id=resolved_id,
|
||||
frames=ordered,
|
||||
transitions=tuple(transitions),
|
||||
total_coherence_delta=float(total),
|
||||
cycle_span=span,
|
||||
)
|
||||
|
||||
|
||||
def _trajectory_id(frames: tuple) -> str:
|
||||
h = hashlib.sha256()
|
||||
for frame in frames:
|
||||
h.update(frame.frame_id.encode("utf-8"))
|
||||
return h.hexdigest()
|
||||
|
|
|
|||
|
|
@ -7,8 +7,11 @@ of neighboring regions — when it bends the field around itself.
|
|||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Tuple
|
||||
import hashlib
|
||||
from dataclasses import dataclass
|
||||
from typing import Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -55,12 +58,51 @@ class SalienceOperator:
|
|||
"""
|
||||
|
||||
def compute(self, regions: Tuple[FieldRegion, ...], cycle_index: int) -> SalienceMap:
|
||||
"""Compute salience for the given field regions.
|
||||
|
||||
Stub: full curvature kernel implemented in Rust hot-path.
|
||||
Python fallback uses pairwise pressure gradient approximation.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"SalienceOperator.compute is a Rust hot-path stub. "
|
||||
"Implement in core_rs::physics::salience or provide Python fallback."
|
||||
"""Compute local curvature by pairwise pressure-gradient deflection."""
|
||||
if not regions:
|
||||
return SalienceMap(entries=(), cycle_index=cycle_index, content_address=_salience_address(()))
|
||||
coords = [np.asarray(region.coordinates, dtype=np.float64) for region in regions]
|
||||
entries: list[SalienceEntry] = []
|
||||
for idx, region in enumerate(regions):
|
||||
gradient = np.zeros_like(coords[idx], dtype=np.float64)
|
||||
curvature = 0.0
|
||||
radius_num = 0.0
|
||||
radius_den = 0.0
|
||||
for jdx, neighbor in enumerate(regions):
|
||||
if idx == jdx:
|
||||
continue
|
||||
delta = coords[jdx] - coords[idx]
|
||||
distance = max(float(np.linalg.norm(delta)), 1e-8)
|
||||
pressure_delta = abs(float(neighbor.pressure_magnitude) - float(region.pressure_magnitude))
|
||||
contribution = pressure_delta / (distance * distance)
|
||||
direction = delta / distance
|
||||
gradient += direction * contribution
|
||||
curvature += contribution
|
||||
radius_num += distance * contribution
|
||||
radius_den += contribution
|
||||
gradient_tuple = tuple(float(v) for v in gradient)
|
||||
entries.append(
|
||||
SalienceEntry(
|
||||
region_id=region.region_id,
|
||||
curvature_magnitude=float(curvature),
|
||||
gradient_vector=gradient_tuple,
|
||||
influence_radius=float(radius_num / radius_den) if radius_den > 0.0 else 0.0,
|
||||
)
|
||||
)
|
||||
ordered = tuple(
|
||||
sorted(entries, key=lambda entry: (-entry.curvature_magnitude, entry.region_id))
|
||||
)
|
||||
return SalienceMap(
|
||||
entries=ordered,
|
||||
cycle_index=cycle_index,
|
||||
content_address=_salience_address(ordered),
|
||||
)
|
||||
|
||||
|
||||
def _salience_address(entries: Tuple[SalienceEntry, ...]) -> str:
|
||||
h = hashlib.sha256()
|
||||
for entry in entries:
|
||||
h.update(entry.region_id.encode("utf-8"))
|
||||
h.update(f":{entry.curvature_magnitude:.12f}:".encode("ascii"))
|
||||
h.update(",".join(f"{v:.12f}" for v in entry.gradient_vector).encode("ascii"))
|
||||
return h.hexdigest()
|
||||
|
|
|
|||
156
core/physics/valence.py
Normal file
156
core/physics/valence.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
"""core.physics.valence — ADR-0007 valence bundle and deterministic lifting."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Mapping
|
||||
|
||||
|
||||
class ForceClass(str, Enum):
|
||||
DECLARATIVE = "declarative"
|
||||
PERFORMATIVE = "performative"
|
||||
IMPERATIVE = "imperative"
|
||||
COHORTATIVE = "cohortative"
|
||||
JUSSIVE = "jussive"
|
||||
INTERROGATIVE = "interrogative"
|
||||
OPTATIVE = "optative"
|
||||
EXPRESSIVE = "expressive"
|
||||
COMMISSIVE = "commissive"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class EmphasisProfile:
|
||||
focus_element: str | None = None
|
||||
mechanism: str = "unmarked"
|
||||
degree: str = "unmarked"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class PolaritySpec:
|
||||
value: str = "positive"
|
||||
kind: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class OrientationSpec:
|
||||
direction: str = "within"
|
||||
target: str | None = None
|
||||
preposition_source: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ValenceBundle:
|
||||
affective: frozenset[str] = field(default_factory=frozenset)
|
||||
force: ForceClass = ForceClass.DECLARATIVE
|
||||
emphasis: EmphasisProfile = field(default_factory=EmphasisProfile)
|
||||
polarity: PolaritySpec = field(default_factory=PolaritySpec)
|
||||
orientation: OrientationSpec = field(default_factory=OrientationSpec)
|
||||
|
||||
def to_payload(self) -> dict[str, object]:
|
||||
payload = asdict(self)
|
||||
payload["affective"] = sorted(self.affective)
|
||||
payload["force"] = self.force.value
|
||||
return payload
|
||||
|
||||
def to_json(self) -> str:
|
||||
return json.dumps(self.to_payload(), sort_keys=True, separators=(",", ":"))
|
||||
|
||||
|
||||
_NEGATIVE_PARTICLES = {
|
||||
"lo": "absolute",
|
||||
"לֹא": "absolute",
|
||||
"al": "prohibitive",
|
||||
"אַל": "prohibitive",
|
||||
"ou": "factual",
|
||||
"οὐ": "factual",
|
||||
"me": "conditional",
|
||||
"μή": "conditional",
|
||||
}
|
||||
|
||||
_ORIENTATION_BY_PREPOSITION = {
|
||||
"pros": "toward",
|
||||
"πρός": "toward",
|
||||
"en": "within",
|
||||
"ἐν": "within",
|
||||
"ek": "from",
|
||||
"ἐκ": "from",
|
||||
"apo": "from",
|
||||
"ἀπό": "from",
|
||||
"dia": "through",
|
||||
"διά": "through",
|
||||
"hypo": "under",
|
||||
"ὑπό": "under",
|
||||
"epi": "upon",
|
||||
"ἐπί": "upon",
|
||||
"para": "alongside",
|
||||
"παρά": "alongside",
|
||||
}
|
||||
|
||||
|
||||
def lift_valence(
|
||||
*,
|
||||
lemma: str,
|
||||
language: str,
|
||||
features: Mapping[str, object] | None = None,
|
||||
notes: str | None = None,
|
||||
) -> ValenceBundle:
|
||||
"""Lift valence deterministically from morphology and pack notes."""
|
||||
features = dict(features or {})
|
||||
lower_lemma = lemma.lower()
|
||||
lower_notes = (notes or "").lower()
|
||||
mood = str(features.get("mood", "")).lower()
|
||||
stem = str(features.get("stem", "")).lower()
|
||||
tense = str(features.get("tense", "")).lower()
|
||||
force = ForceClass.DECLARATIVE
|
||||
if "divine" in lower_notes and ("create" in lower_notes or "creation" in lower_notes):
|
||||
force = ForceClass.PERFORMATIVE
|
||||
elif mood == "imperative":
|
||||
force = ForceClass.IMPERATIVE
|
||||
elif mood == "cohortative":
|
||||
force = ForceClass.COHORTATIVE
|
||||
elif mood == "jussive":
|
||||
force = ForceClass.JUSSIVE
|
||||
elif mood == "optative":
|
||||
force = ForceClass.OPTATIVE
|
||||
|
||||
affective: set[str] = set()
|
||||
if "divine" in lower_notes or lower_lemma in {"θεός", "god"}:
|
||||
affective.add("awe")
|
||||
if "truth" in lower_notes or lower_lemma in {"אמת", "ἀλήθεια", "truth"}:
|
||||
affective.add("peace")
|
||||
if "life" in lower_notes or lower_lemma in {"ζωή", "life"}:
|
||||
affective.add("exultation")
|
||||
|
||||
mechanism = "unmarked"
|
||||
degree = "unmarked"
|
||||
if stem in {"piel", "intensive"}:
|
||||
mechanism = "stem_intensification"
|
||||
degree = "strong"
|
||||
elif "front" in lower_notes:
|
||||
mechanism = "fronting"
|
||||
degree = "strong"
|
||||
elif "anarthrous" in lower_notes:
|
||||
mechanism = "particle"
|
||||
degree = "light"
|
||||
|
||||
neg_kind = _NEGATIVE_PARTICLES.get(lower_lemma)
|
||||
polarity = PolaritySpec(
|
||||
value="negative" if neg_kind else "positive",
|
||||
kind=neg_kind,
|
||||
)
|
||||
direction = _ORIENTATION_BY_PREPOSITION.get(lower_lemma, "within")
|
||||
orientation = OrientationSpec(
|
||||
direction=direction,
|
||||
preposition_source=lemma if direction != "within" or lower_lemma in _ORIENTATION_BY_PREPOSITION else None,
|
||||
)
|
||||
if tense in {"imperfect", "present"} and force is ForceClass.DECLARATIVE:
|
||||
degree = "light" if degree == "unmarked" else degree
|
||||
return ValenceBundle(
|
||||
affective=frozenset(affective),
|
||||
force=force,
|
||||
emphasis=EmphasisProfile(focus_element=lemma, mechanism=mechanism, degree=degree),
|
||||
polarity=polarity,
|
||||
orientation=orientation,
|
||||
)
|
||||
|
|
@ -29,7 +29,6 @@ from typing import TYPE_CHECKING, Sequence
|
|||
|
||||
from core_ingest.types import (
|
||||
CandidateGeometricPressure,
|
||||
DeterminismClass,
|
||||
GateDisposition,
|
||||
LearningArtifact,
|
||||
ReviewDecision,
|
||||
|
|
@ -90,9 +89,20 @@ class SemanticGate:
|
|||
def check(self, packet: CandidateGeometricPressure) -> str | None:
|
||||
import json
|
||||
from core_ingest.types import Modality
|
||||
from core.physics.energy import EnergyClass
|
||||
|
||||
if packet.payload_json in ("{}", ""):
|
||||
return "payload_json is empty — no content to ingest"
|
||||
payload = json.loads(packet.payload_json)
|
||||
if "energy_class_hint" in payload:
|
||||
try:
|
||||
EnergyClass(str(payload["energy_class_hint"]))
|
||||
except ValueError:
|
||||
return f"invalid energy_class_hint: {payload['energy_class_hint']!r}"
|
||||
if "valence" in payload:
|
||||
failure = _validate_valence_payload(payload["valence"])
|
||||
if failure is not None:
|
||||
return failure
|
||||
|
||||
# Require non-empty lemma for text / scripture
|
||||
if packet.modality in (Modality.TEXT, Modality.SCRIPTURE) and not packet.lemma:
|
||||
|
|
@ -137,8 +147,17 @@ class GovernanceGate:
|
|||
packet: CandidateGeometricPressure,
|
||||
authorized_ids: frozenset[str],
|
||||
) -> GateDisposition:
|
||||
import json
|
||||
|
||||
if packet.review_level == ReviewLevel.AUTO_REJECT:
|
||||
return GateDisposition.REJECTED_GOVERNANCE
|
||||
payload = json.loads(packet.payload_json)
|
||||
if payload.get("energy_class_hint") == "E4":
|
||||
if packet.review_level != ReviewLevel.ARCHITECT_REVIEW_REQUIRED:
|
||||
return GateDisposition.REJECTED_GOVERNANCE
|
||||
if packet.pressure_id in authorized_ids:
|
||||
return GateDisposition.OVERRIDE_ACCEPTED
|
||||
return GateDisposition.REJECTED_GOVERNANCE
|
||||
|
||||
# Override: an authorized ReviewDecision accepts regardless of level
|
||||
if packet.pressure_id in authorized_ids:
|
||||
|
|
@ -155,6 +174,21 @@ class GovernanceGate:
|
|||
return GateDisposition.REJECTED_GOVERNANCE
|
||||
|
||||
|
||||
def _validate_valence_payload(valence: object) -> str | None:
|
||||
if not isinstance(valence, dict):
|
||||
return "valence must be an object"
|
||||
required = {"affective", "force", "emphasis", "polarity", "orientation"}
|
||||
missing = required - set(valence)
|
||||
if missing:
|
||||
return f"valence missing required channel(s): {', '.join(sorted(missing))}"
|
||||
if not isinstance(valence["affective"], list):
|
||||
return "valence.affective must be a list"
|
||||
for key in ("emphasis", "polarity", "orientation"):
|
||||
if not isinstance(valence[key], dict):
|
||||
return f"valence.{key} must be an object"
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Compiler
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -21,4 +21,11 @@ def propagate_step(state: FieldState, V) -> FieldState:
|
|||
Returns a new FieldState one step forward on the manifold.
|
||||
"""
|
||||
new_F = versor_apply(V, state.F)
|
||||
return FieldState(F=new_F, node=state.node, step=state.step + 1, holonomy=state.holonomy)
|
||||
return FieldState(
|
||||
F=new_F,
|
||||
node=state.node,
|
||||
step=state.step + 1,
|
||||
holonomy=state.holonomy,
|
||||
energy=state.energy,
|
||||
valence=state.valence,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,8 +12,13 @@ reference to the array passed in and expect coherence.
|
|||
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
import numpy as np
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.physics.energy import EnergyProfile
|
||||
from core.physics.valence import ValenceBundle
|
||||
|
||||
_EXPECTED_COMPONENTS = 32
|
||||
|
||||
|
||||
|
|
@ -23,6 +28,8 @@ class FieldState:
|
|||
node: int = 0 # current node index in the vocabulary manifold
|
||||
step: int = 0 # number of propagation steps taken
|
||||
holonomy: np.ndarray | None = None
|
||||
energy: EnergyProfile | None = None
|
||||
valence: ValenceBundle | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
# Enforce copy + dtype + shape at the construction boundary.
|
||||
|
|
@ -54,4 +61,11 @@ class FieldState:
|
|||
|
||||
def advance(self, new_F: np.ndarray, new_node: int) -> FieldState:
|
||||
"""Return a new FieldState after one propagation step."""
|
||||
return FieldState(F=new_F, node=new_node, step=self.step + 1, holonomy=self.holonomy)
|
||||
return FieldState(
|
||||
F=new_F,
|
||||
node=new_node,
|
||||
step=self.step + 1,
|
||||
holonomy=self.holonomy,
|
||||
energy=self.energy,
|
||||
valence=self.valence,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from dataclasses import dataclass
|
|||
import numpy as np
|
||||
|
||||
from algebra.backend import cga_inner
|
||||
from core.physics.salience import FieldRegion, SalienceOperator as CurvatureSalienceOperator
|
||||
from field.state import FieldState
|
||||
from vocab.manifold import VocabManifold
|
||||
|
||||
|
|
@ -23,13 +24,11 @@ class SalienceMap:
|
|||
|
||||
class SalienceOperator:
|
||||
"""
|
||||
Compute geometric salience of manifold points relative to current FieldState.
|
||||
Compute generation-facing salience from ADR-0008 field curvature.
|
||||
|
||||
Salience is field-relative CGA activation:
|
||||
salience(v_i) = |cga_inner(F, v_i)| / (||F|| * ||v_i||)
|
||||
|
||||
No learned weights. No softmax. Pure geometry routed through algebra.backend,
|
||||
which uses core_rs when active.
|
||||
The live API still returns manifold indices for generation, but the score is
|
||||
now a local curvature magnitude from core.physics.salience rather than
|
||||
normalized proximity to the query field.
|
||||
"""
|
||||
|
||||
def compute(self, field: FieldState, vocab: VocabManifold, top_k: int = 16) -> SalienceMap:
|
||||
|
|
@ -38,15 +37,26 @@ class SalienceOperator:
|
|||
if len(vocab) == 0:
|
||||
return SalienceMap(indices=np.asarray([], dtype=np.int64), scores=np.asarray([], dtype=np.float32), budget=0)
|
||||
|
||||
query = np.asarray(field.F, dtype=np.float32)
|
||||
query_norm = max(float(np.linalg.norm(query)), 1e-8)
|
||||
scores: list[float] = []
|
||||
active = vocab.get_versor_at(field.node)
|
||||
regions: list[FieldRegion] = []
|
||||
for idx in range(len(vocab)):
|
||||
v = vocab.get_versor_at(idx)
|
||||
denom = query_norm * max(float(np.linalg.norm(v)), 1e-8)
|
||||
scores.append(abs(float(cga_inner(query, v))) / denom)
|
||||
energy = vocab.energy_for_word(vocab.get_word_at(idx))
|
||||
baseline = energy.raw if energy is not None else 0.1
|
||||
active_distance = max(0.0, -2.0 * float(cga_inner(active, v)))
|
||||
pressure = baseline + (1.0 / (1.0 + active_distance))
|
||||
regions.append(
|
||||
FieldRegion(
|
||||
region_id=str(idx),
|
||||
coordinates=tuple(float(x) for x in np.asarray(v, dtype=np.float32)),
|
||||
pressure_magnitude=pressure,
|
||||
)
|
||||
)
|
||||
|
||||
scores_arr = np.asarray(scores, dtype=np.float32)
|
||||
curvature = CurvatureSalienceOperator().compute(tuple(regions), cycle_index=field.step)
|
||||
scores_arr = np.zeros(len(vocab), dtype=np.float32)
|
||||
for entry in curvature.entries:
|
||||
scores_arr[int(entry.region_id)] = float(entry.curvature_magnitude)
|
||||
k = min(int(top_k), len(vocab))
|
||||
order = np.argsort(-scores_arr, kind="stable")[:k]
|
||||
return SalienceMap(indices=order.astype(np.int64), scores=scores_arr[order], budget=k)
|
||||
|
|
|
|||
|
|
@ -80,15 +80,46 @@ def _nearest_next(
|
|||
)
|
||||
for extra in fallback_orders:
|
||||
try:
|
||||
return vocab.nearest(
|
||||
return _nearest_with_optional_candidates(
|
||||
vocab,
|
||||
F_voiced,
|
||||
exclude_idx=current_node,
|
||||
exclude_indices=extra,
|
||||
candidate_indices=candidate_indices,
|
||||
current_node,
|
||||
extra,
|
||||
candidate_indices,
|
||||
)
|
||||
except ValueError:
|
||||
continue
|
||||
return vocab.nearest(F_voiced, candidate_indices=candidate_indices)
|
||||
return _nearest_with_optional_candidates(
|
||||
vocab,
|
||||
F_voiced,
|
||||
-1,
|
||||
set(),
|
||||
candidate_indices,
|
||||
)
|
||||
|
||||
|
||||
def _nearest_with_optional_candidates(
|
||||
vocab,
|
||||
F_voiced,
|
||||
current_node: int,
|
||||
exclude_indices: set[int],
|
||||
candidate_indices: np.ndarray | None,
|
||||
) -> tuple[str, int]:
|
||||
try:
|
||||
return vocab.nearest(
|
||||
F_voiced,
|
||||
exclude_idx=current_node,
|
||||
exclude_indices=exclude_indices,
|
||||
candidate_indices=candidate_indices,
|
||||
)
|
||||
except TypeError:
|
||||
if candidate_indices is not None:
|
||||
raise
|
||||
return vocab.nearest(
|
||||
F_voiced,
|
||||
exclude_idx=current_node,
|
||||
exclude_indices=exclude_indices,
|
||||
)
|
||||
|
||||
|
||||
def _voiced_state(state: FieldState, persona) -> FieldState:
|
||||
|
|
@ -98,6 +129,8 @@ def _voiced_state(state: FieldState, persona) -> FieldState:
|
|||
node=state.node,
|
||||
step=state.step,
|
||||
holonomy=state.holonomy,
|
||||
energy=state.energy,
|
||||
valence=state.valence,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -121,6 +154,8 @@ def _recall_state(state: FieldState, vault, top_k: int) -> FieldState:
|
|||
node=state.node,
|
||||
step=current.step,
|
||||
holonomy=state.holonomy,
|
||||
energy=state.energy,
|
||||
valence=state.valence,
|
||||
)
|
||||
return current
|
||||
|
||||
|
|
@ -207,7 +242,7 @@ def generate(
|
|||
)
|
||||
candidate_indices = _intersect_candidates(language_candidates, salience_candidates)
|
||||
if candidate_indices is not None and len(candidate_indices) == 0:
|
||||
candidate_indices = language_candidates if language_candidates is not None else salience_candidates
|
||||
candidate_indices = salience_candidates if salience_candidates is not None else language_candidates
|
||||
candidates_used = None if candidate_indices is None else len(candidate_indices)
|
||||
|
||||
stop_nodes = frozenset(
|
||||
|
|
@ -237,7 +272,14 @@ def generate(
|
|||
V = word_transition_rotor(A, B)
|
||||
|
||||
current = propagate_step(current, V)
|
||||
current = FieldState(F=current.F, node=word_idx, step=current.step, holonomy=current.holonomy)
|
||||
current = FieldState(
|
||||
F=current.F,
|
||||
node=word_idx,
|
||||
step=current.step,
|
||||
holonomy=current.holonomy,
|
||||
energy=current.energy,
|
||||
valence=current.valence,
|
||||
)
|
||||
recent_nodes.append(word_idx)
|
||||
|
||||
return GenerationResult(
|
||||
|
|
@ -288,5 +330,12 @@ async def agenerate(
|
|||
V = word_transition_rotor(A, B)
|
||||
|
||||
current = propagate_step(current, V)
|
||||
current = FieldState(F=current.F, node=word_idx, step=current.step, holonomy=current.holonomy)
|
||||
current = FieldState(
|
||||
F=current.F,
|
||||
node=word_idx,
|
||||
step=current.step,
|
||||
holonomy=current.holonomy,
|
||||
energy=current.energy,
|
||||
valence=current.valence,
|
||||
)
|
||||
recent_nodes.append(word_idx)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ import numpy as np
|
|||
|
||||
from algebra.cl41 import geometric_product
|
||||
from algebra.versor import normalize_to_versor, versor_condition
|
||||
from core.physics.energy import FieldEnergyOperator, EnergyClass
|
||||
from core.physics.valence import ValenceBundle
|
||||
from algebra.holonomy import holonomy_encode
|
||||
from field.state import FieldState
|
||||
from language_packs.schema import MorphologyEntry
|
||||
|
|
@ -225,6 +227,66 @@ def _lookup_or_ground(token: str, vocab) -> np.ndarray:
|
|||
return _ground_unknown_token(token, vocab)
|
||||
|
||||
|
||||
def _field_energy(tokens: list, vocab) -> object | None:
|
||||
energy_for_word = getattr(vocab, "energy_for_word", None)
|
||||
morphology_for_word = getattr(vocab, "morphology_for_word", None)
|
||||
if energy_for_word is None:
|
||||
return None
|
||||
profiles = [energy_for_word(token) for token in tokens]
|
||||
profiles = [profile for profile in profiles if profile is not None]
|
||||
features: dict[str, object] = {}
|
||||
if morphology_for_word is not None:
|
||||
for token in tokens:
|
||||
morphology = morphology_for_word(token)
|
||||
if morphology is not None:
|
||||
features.update(dict(morphology.inflection))
|
||||
if morphology.stem:
|
||||
features.setdefault("stem", morphology.stem)
|
||||
if not profiles and not features:
|
||||
return None
|
||||
max_class = max((profile.energy_class for profile in profiles), default=EnergyClass.E0, key=lambda cls: int(cls.value[1]))
|
||||
residual = max((profile.coherence_residual for profile in profiles), default=0.0)
|
||||
convergence = sum(profile.convergence_density for profile in profiles) or len(tokens)
|
||||
activation = sum(profile.activation_count for profile in profiles) or 1
|
||||
anchor_adjacent = any(profile.anchor_adjacent for profile in profiles)
|
||||
computed = FieldEnergyOperator().compute(
|
||||
convergence_density=convergence,
|
||||
activation_count=activation,
|
||||
morphology_features=features,
|
||||
anchor_adjacent=anchor_adjacent,
|
||||
coherence_residual=residual,
|
||||
)
|
||||
return computed if int(computed.energy_class.value[1]) >= int(max_class.value[1]) else max(profiles, key=lambda profile: int(profile.energy_class.value[1]))
|
||||
|
||||
|
||||
def _field_valence(tokens: list, vocab) -> ValenceBundle | None:
|
||||
valence_for_word = getattr(vocab, "valence_for_word", None)
|
||||
if valence_for_word is None:
|
||||
return None
|
||||
bundles = [valence_for_word(token) for token in tokens]
|
||||
bundles = [bundle for bundle in bundles if bundle is not None]
|
||||
if not bundles:
|
||||
return None
|
||||
affective: set[str] = set()
|
||||
for bundle in bundles:
|
||||
affective.update(bundle.affective)
|
||||
strongest = max(
|
||||
bundles,
|
||||
key=lambda bundle: (
|
||||
bundle.force.value != "declarative",
|
||||
bundle.emphasis.degree in {"strong", "absolute"},
|
||||
len(bundle.affective),
|
||||
),
|
||||
)
|
||||
return ValenceBundle(
|
||||
affective=frozenset(affective),
|
||||
force=strongest.force,
|
||||
emphasis=strongest.emphasis,
|
||||
polarity=strongest.polarity,
|
||||
orientation=strongest.orientation,
|
||||
)
|
||||
|
||||
|
||||
def inject(tokens: list, vocab) -> FieldState:
|
||||
"""
|
||||
Encode a token sequence and inject into the versor manifold.
|
||||
|
|
@ -246,4 +308,4 @@ def inject(tokens: list, vocab) -> FieldState:
|
|||
"Check holonomy_encode() and normalize_to_versor()."
|
||||
)
|
||||
|
||||
return FieldState(F=F, node=0, step=0, holonomy=H)
|
||||
return FieldState(F=F, node=0, step=0, holonomy=H, energy=_field_energy(tokens, vocab), valence=_field_valence(tokens, vocab))
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import numpy as np
|
|||
|
||||
from algebra.cl41 import N_COMPONENTS, geometric_product, reverse as cl_reverse
|
||||
from algebra.versor import unitize_versor
|
||||
from core.physics.energy import FieldEnergyOperator
|
||||
from core.physics.valence import lift_valence
|
||||
from language_packs.schema import (
|
||||
LanguagePackManifest,
|
||||
LanguageRole,
|
||||
|
|
@ -28,6 +30,7 @@ _MORPHOLOGY_CLUSTER_NUDGE_STRENGTH: float = 0.40
|
|||
_PRIMARY_SEMANTIC_DOMAIN_WEIGHT: float = 0.55
|
||||
_LOGOS_PARTICIPATION_WEIGHT: float = 0.25
|
||||
_FEATURE_COMPONENTS: tuple[int, ...] = (6, 7, 9, 10, 12, 14)
|
||||
_ENERGY = FieldEnergyOperator()
|
||||
|
||||
|
||||
def _hash_to_blade(name: str, salt: str) -> int:
|
||||
|
|
@ -271,7 +274,28 @@ def compile_entries_to_manifold(entries: list[LexicalEntry], morphology_registry
|
|||
for entry in entries:
|
||||
morphology = _resolved_morphology(entry, morphology_registry)
|
||||
versor = _entry_to_coordinate(entry, morphology)
|
||||
manifold.add(entry.surface, versor, morphology=morphology, language=entry.language)
|
||||
features = dict(morphology.inflection) if morphology is not None else {}
|
||||
if morphology is not None and morphology.stem:
|
||||
features.setdefault("stem", morphology.stem)
|
||||
energy = _ENERGY.compute(
|
||||
convergence_density=max(1, len(entry.provenance_ids)),
|
||||
activation_count=1,
|
||||
morphology_features=features,
|
||||
anchor_adjacent=_has_logos_participation(entry.semantic_domains),
|
||||
)
|
||||
valence = lift_valence(
|
||||
lemma=entry.lemma or entry.surface,
|
||||
language=entry.language,
|
||||
features=features,
|
||||
)
|
||||
manifold.add(
|
||||
entry.surface,
|
||||
versor,
|
||||
morphology=morphology,
|
||||
language=entry.language,
|
||||
energy=energy,
|
||||
valence=valence,
|
||||
)
|
||||
entry_id_to_surface[entry.entry_id] = entry.surface
|
||||
|
||||
if morphology_registry is not None:
|
||||
|
|
@ -403,6 +427,8 @@ def load_mounted_packs(pack_ids: tuple[str, ...] | list[str]) -> VocabManifold:
|
|||
manifold.get_versor_at(idx),
|
||||
morphology=manifold.morphology_for_word(surface),
|
||||
language=None if entry is None else entry.language,
|
||||
energy=manifold.energy_for_word(surface),
|
||||
valence=manifold.valence_for_word(surface),
|
||||
)
|
||||
if entry is not None and entry.semantic_domains:
|
||||
primary_groups.setdefault(entry.semantic_domains[0].lower(), []).append(
|
||||
|
|
|
|||
52
language_packs/evidence.py
Normal file
52
language_packs/evidence.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
"""Measured holonomy-resonance evidence helpers for ADR-0015."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
|
||||
from algebra.cga import cga_inner
|
||||
from algebra.holonomy import holonomy_encode
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ResonanceEvidence:
|
||||
case_id: str
|
||||
aligned_score: float
|
||||
contrast_score: float
|
||||
|
||||
@property
|
||||
def passes(self) -> bool:
|
||||
return self.aligned_score > self.contrast_score
|
||||
|
||||
|
||||
def encode_clause(manifold, tokens: tuple[str, ...] | list[str]) -> np.ndarray:
|
||||
return holonomy_encode([manifold.get_versor(token) for token in tokens])
|
||||
|
||||
|
||||
def mean_pair_score(manifold, pairs: tuple[tuple[str, str], ...]) -> float:
|
||||
if not pairs:
|
||||
return 0.0
|
||||
return float(
|
||||
np.mean(
|
||||
[
|
||||
cga_inner(manifold.get_versor(left), manifold.get_versor(right))
|
||||
for left, right in pairs
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def resonance_evidence(
|
||||
*,
|
||||
case_id: str,
|
||||
manifold,
|
||||
aligned_pairs: tuple[tuple[str, str], ...],
|
||||
contrast_pairs: tuple[tuple[str, str], ...],
|
||||
) -> ResonanceEvidence:
|
||||
return ResonanceEvidence(
|
||||
case_id=case_id,
|
||||
aligned_score=mean_pair_score(manifold, aligned_pairs),
|
||||
contrast_score=mean_pair_score(manifold, contrast_pairs),
|
||||
)
|
||||
122
packs/common/runtime_rules.py
Normal file
122
packs/common/runtime_rules.py
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
"""Deterministic runtime helpers for local language-pack rules."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from core.physics.energy import EnergyClass
|
||||
from core.physics.valence import lift_valence
|
||||
from core_ingest.types import (
|
||||
CandidateGeometricPressure,
|
||||
DeterminismClass,
|
||||
FrontendTrace,
|
||||
Modality,
|
||||
ReviewLevel,
|
||||
SourceSpan,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class SurfaceRealization:
|
||||
surface: str
|
||||
language: str
|
||||
field_target: str | None = None
|
||||
energy_class: str | None = None
|
||||
valence: dict[str, object] | None = None
|
||||
|
||||
|
||||
def read_jsonl(path: Path) -> list[dict[str, Any]]:
|
||||
return [
|
||||
json.loads(line)
|
||||
for line in path.read_text(encoding="utf-8").splitlines()
|
||||
if line.strip()
|
||||
]
|
||||
|
||||
|
||||
def analysis_payload(analysis: object) -> dict[str, Any]:
|
||||
if isinstance(analysis, dict):
|
||||
payload = dict(analysis.get("input", analysis))
|
||||
else:
|
||||
payload = dict(getattr(analysis, "__dict__", {}))
|
||||
if not payload:
|
||||
raise ValueError("analysis must expose lemma_id or sense_id fields")
|
||||
return payload
|
||||
|
||||
|
||||
def lift_from_pack(pack_dir: Path, analysis: object, *, language: str) -> list[CandidateGeometricPressure]:
|
||||
payload = analysis_payload(analysis)
|
||||
senses = {record["sense_id"]: record for record in read_jsonl(pack_dir / "senses.jsonl")}
|
||||
lemmas = {record["lemma_id"]: record for record in read_jsonl(pack_dir / "lemmas.jsonl")}
|
||||
sense = senses.get(str(payload.get("sense_id", "")))
|
||||
lemma_id = str(payload.get("lemma_id") or (sense or {}).get("lemma_id") or "")
|
||||
lemma = lemmas.get(lemma_id)
|
||||
if lemma is None:
|
||||
raise KeyError(f"unknown lemma_id: {lemma_id}")
|
||||
field_target = str((sense or {}).get("field_target") or lemma["field_hooks"][0])
|
||||
pressure_kind = str(payload.get("pressure_kind", "semantic"))
|
||||
features = {
|
||||
"morph_class": lemma.get("morph_class", ""),
|
||||
"semantic_family": lemma.get("semantic_family", ""),
|
||||
}
|
||||
valence = lift_valence(
|
||||
lemma=str(lemma["script_form"]),
|
||||
language=language,
|
||||
features=features,
|
||||
).to_payload()
|
||||
packet_payload = {
|
||||
"field_target": field_target,
|
||||
"pressure_kind": pressure_kind,
|
||||
"energy_class_hint": EnergyClass.E2.value,
|
||||
"valence": valence,
|
||||
"source": {
|
||||
"lemma_id": lemma_id,
|
||||
"sense_id": payload.get("sense_id"),
|
||||
"frame_id": payload.get("frame_id"),
|
||||
},
|
||||
}
|
||||
canonical = json.dumps(packet_payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
|
||||
digest = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
||||
span = SourceSpan(byte_start=0, byte_end=max(1, len(canonical.encode("utf-8"))), source_sha256=digest)
|
||||
packet = CandidateGeometricPressure(
|
||||
kind=pressure_kind,
|
||||
modality=Modality.SCRIPTURE if language in {"he", "el", "grc"} else Modality.TEXT,
|
||||
provenance=(span,),
|
||||
frontend=FrontendTrace(
|
||||
instrument_id=f"{language}.lift_rules",
|
||||
determinism=DeterminismClass.D0,
|
||||
version="1.0.0",
|
||||
),
|
||||
review_level=ReviewLevel.AUTO_ACCEPT_ELIGIBLE,
|
||||
confidence=1.0,
|
||||
uncertainty=0.0,
|
||||
lemma=str(lemma["script_form"]),
|
||||
payload_json=canonical,
|
||||
)
|
||||
return [packet]
|
||||
|
||||
|
||||
def readback_from_intent(field_state: object, intent: object, *, language: str) -> SurfaceRealization:
|
||||
payload = analysis_payload(intent or {"surface": ""})
|
||||
surface = payload.get("surface")
|
||||
if surface is None and "tokens" in payload:
|
||||
surface = " ".join(str(token) for token in payload["tokens"])
|
||||
if surface is None and "lemma" in payload:
|
||||
surface = str(payload["lemma"])
|
||||
if surface is None and "script_form" in payload:
|
||||
surface = str(payload["script_form"])
|
||||
if surface is None:
|
||||
energy = getattr(field_state, "energy", None)
|
||||
surface = energy.energy_class.value if energy is not None else ""
|
||||
energy = getattr(field_state, "energy", None)
|
||||
valence = getattr(field_state, "valence", None)
|
||||
return SurfaceRealization(
|
||||
surface=str(surface),
|
||||
language=language,
|
||||
field_target=payload.get("field_target"),
|
||||
energy_class=None if energy is None else energy.energy_class.value,
|
||||
valence=None if valence is None else valence.to_payload(),
|
||||
)
|
||||
133
packs/common/validator.py
Normal file
133
packs/common/validator.py
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
"""Executable validation gates for local language packs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
|
||||
from packs.common.runtime_rules import read_jsonl
|
||||
|
||||
|
||||
def _load_module(path: Path, name: str) -> ModuleType:
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
if spec is None or spec.loader is None:
|
||||
raise ImportError(f"cannot load {path}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def _gate_schema(pack_dir: Path) -> tuple[bool, str]:
|
||||
required = ("lemmas.jsonl", "senses.jsonl", "morphology.jsonl", "probes/basic.jsonl")
|
||||
for rel in required:
|
||||
path = pack_dir / rel
|
||||
if not path.exists():
|
||||
return False, f"missing {rel}"
|
||||
try:
|
||||
read_jsonl(path)
|
||||
except json.JSONDecodeError as exc:
|
||||
return False, f"{rel}: invalid JSONL: {exc}"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_lexical(pack_dir: Path) -> tuple[bool, str]:
|
||||
seen = set()
|
||||
for record in read_jsonl(pack_dir / "lemmas.jsonl"):
|
||||
lid = record["lemma_id"]
|
||||
if lid in seen:
|
||||
return False, f"duplicate lemma_id: {lid}"
|
||||
seen.add(lid)
|
||||
if not record.get("field_hooks"):
|
||||
return False, f"lemma has no field_hooks: {lid}"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_morphology(pack_dir: Path) -> tuple[bool, str]:
|
||||
known = {record["lemma_id"] for record in read_jsonl(pack_dir / "lemmas.jsonl")}
|
||||
for record in read_jsonl(pack_dir / "morphology.jsonl"):
|
||||
if record["lemma_id"] not in known:
|
||||
return False, f"unknown lemma_id in morphology: {record['lemma_id']}"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_lift(pack_dir: Path) -> tuple[bool, str]:
|
||||
lift_rules = _load_module(pack_dir / "lift_rules.py", f"{pack_dir.name}_lift_rules")
|
||||
for probe in read_jsonl(pack_dir / "probes" / "basic.jsonl"):
|
||||
if probe.get("kind") != "lift":
|
||||
continue
|
||||
packet = lift_rules.lift(probe["input"])[0]
|
||||
payload = json.loads(packet.payload_json)
|
||||
expected = probe["expected"]
|
||||
for key in ("field_target", "pressure_kind"):
|
||||
if key in expected and payload.get(key) != expected[key]:
|
||||
return False, f"{probe['probe_id']}: {key} {payload.get(key)!r} != {expected[key]!r}"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_readback(pack_dir: Path, language: str) -> tuple[bool, str]:
|
||||
readback_rules = _load_module(pack_dir / "readback_rules.py", f"{pack_dir.name}_readback_rules")
|
||||
surface = readback_rules.readback(None, {"surface": "probe", "field_target": "test"})
|
||||
if getattr(surface, "surface", "") != "probe":
|
||||
return False, "readback did not preserve requested surface"
|
||||
if getattr(surface, "language", "") != language:
|
||||
return False, "readback returned wrong language"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_determinism(pack_dir: Path) -> tuple[bool, str]:
|
||||
lift_rules = _load_module(pack_dir / "lift_rules.py", f"{pack_dir.name}_lift_rules_det")
|
||||
for probe in read_jsonl(pack_dir / "probes" / "basic.jsonl"):
|
||||
if probe.get("kind") == "lift":
|
||||
left = lift_rules.lift(probe["input"])[0].payload_json
|
||||
right = lift_rules.lift(probe["input"])[0].payload_json
|
||||
if left != right:
|
||||
return False, f"{probe['probe_id']}: lift is nondeterministic"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_alignment(pack_dir: Path) -> tuple[bool, str]:
|
||||
lemmas = {record["lemma_id"] for record in read_jsonl(pack_dir / "lemmas.jsonl")}
|
||||
senses = {record["sense_id"] for record in read_jsonl(pack_dir / "senses.jsonl")}
|
||||
for probe in read_jsonl(pack_dir / "probes" / "basic.jsonl"):
|
||||
if probe.get("kind") != "alignment":
|
||||
continue
|
||||
expected = probe["expected"]
|
||||
if expected.get("lemma_id") not in lemmas:
|
||||
return False, f"{probe['probe_id']}: unknown lemma anchor"
|
||||
if expected.get("sense_id") not in senses:
|
||||
return False, f"{probe['probe_id']}: unknown sense anchor"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_coverage(pack_dir: Path) -> tuple[bool, str]:
|
||||
covered = {probe["kind"] for probe in read_jsonl(pack_dir / "probes" / "basic.jsonl")}
|
||||
required = {"normalize", "lift", "alignment"}
|
||||
missing = required - covered
|
||||
if missing:
|
||||
return False, f"missing probe kind(s): {', '.join(sorted(missing))}"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def validate_pack_dir(pack_dir: Path, *, pack_id: str, language: str) -> dict:
|
||||
gates = (
|
||||
("schema", lambda: _gate_schema(pack_dir)),
|
||||
("lexical", lambda: _gate_lexical(pack_dir)),
|
||||
("morphology", lambda: _gate_morphology(pack_dir)),
|
||||
("lift", lambda: _gate_lift(pack_dir)),
|
||||
("readback", lambda: _gate_readback(pack_dir, language)),
|
||||
("determinism", lambda: _gate_determinism(pack_dir)),
|
||||
("alignment", lambda: _gate_alignment(pack_dir)),
|
||||
("coverage", lambda: _gate_coverage(pack_dir)),
|
||||
)
|
||||
report = {"pack_id": pack_id, "active": False, "gates": {}}
|
||||
for index, (name, gate_fn) in enumerate(gates):
|
||||
passed, reason = gate_fn()
|
||||
report["gates"][name] = {"passed": passed, "reason": reason}
|
||||
if not passed:
|
||||
for remaining_name, _ in gates[index + 1:]:
|
||||
report["gates"][remaining_name] = {"passed": False, "reason": "blocked by prior gate failure"}
|
||||
return report
|
||||
report["active"] = True
|
||||
return report
|
||||
|
|
@ -1,42 +1,14 @@
|
|||
"""
|
||||
Lift rules for the Koine Greek depth pack.
|
||||
|
||||
Responsibility: receive a LinguisticAnalysis from the el pack analyzer
|
||||
and return a CandidatePressureBatch.
|
||||
|
||||
Koine Greek-specific lift requirements:
|
||||
- Tense-aspect: Greek tense encodes both time and aspect. The imperfect
|
||||
of eimi (en) lifts into existence.state.continuous, not existence.state.
|
||||
The aorist would lift into existence.event. These are not interchangeable.
|
||||
- Article system: the presence or absence of the article on a predicate
|
||||
nominative is semantically load-bearing (Colwell construction).
|
||||
Anarthrous predicate nominatives must lift with a qualitative tag.
|
||||
- Voice: middle voice is not passive. Middle voice lifts carry a
|
||||
reflexive or self-involving semantic that must be preserved.
|
||||
- Pros with accusative: lifts into relation.presence-toward,
|
||||
not relation.accompaniment. The preposition selects the sense.
|
||||
|
||||
Current status:
|
||||
Blocked on LinguisticAnalysis contract (el pack specific: must carry
|
||||
full tense-aspect-voice-mood bundle and article resolution).
|
||||
"""
|
||||
"""Deterministic lift rules for the Koine Greek depth pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core_ingest.pressure import CandidateGeometricPressure
|
||||
from pathlib import Path
|
||||
|
||||
from core_ingest.types import CandidateGeometricPressure
|
||||
from packs.common.runtime_rules import lift_from_pack
|
||||
|
||||
PACK_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def lift(analysis: object) -> list["CandidateGeometricPressure"]:
|
||||
"""
|
||||
Lift a Greek LinguisticAnalysis into CandidateGeometricPressure packets.
|
||||
|
||||
Blocked on: el pack LinguisticAnalysis contract — must carry
|
||||
tense-aspect-voice-mood bundle and article resolution (arthrous/anarthrous)
|
||||
before this can be implemented correctly.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"el:lift — LinguisticAnalysis contract for Koine Greek not yet finalized. "
|
||||
"Must carry tense, aspect, voice, mood, and article resolution."
|
||||
)
|
||||
def lift(analysis: object) -> list[CandidateGeometricPressure]:
|
||||
return lift_from_pack(PACK_DIR, analysis, language="el")
|
||||
|
|
|
|||
|
|
@ -1,37 +1,9 @@
|
|||
"""
|
||||
Readback rules for the Koine Greek depth pack.
|
||||
|
||||
Koine Greek readback produces grammatical Biblical Greek surface realizations.
|
||||
This is a depth-pack operation invoked only when the model targets Greek articulation.
|
||||
|
||||
Koine Greek-specific readback requirements:
|
||||
- Full inflection: case, number, gender on nouns/adjectives/articles;
|
||||
tense, voice, mood, person, number on verbs. Every word fully inflected.
|
||||
- Accent placement: Greek accents must be placed correctly.
|
||||
Unaccented output is not a valid surface realization.
|
||||
- Article resolution: the article must be present or absent based on
|
||||
the semantic role of the noun in the clause. Anarthrous predicates
|
||||
in copular constructions must remain anarthrous (Colwell).
|
||||
- Aspect selection: aorist vs. imperfect vs. perfect is not stylistic.
|
||||
Each carries distinct semantic content that must be honored.
|
||||
|
||||
Current status:
|
||||
Blocked on FieldState and SurfaceRealization types.
|
||||
"""
|
||||
"""Deterministic readback rules for the Koine Greek depth pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from packs.common.runtime_rules import SurfaceRealization, readback_from_intent
|
||||
|
||||
def readback(field_state: object, intent: object = None) -> object:
|
||||
"""
|
||||
Produce a grammatical Koine Greek surface realization from a field state.
|
||||
|
||||
Blocked on: FieldState and SurfaceRealization types.
|
||||
When implemented: must produce fully inflected and accented Greek output
|
||||
with correct article placement and tense-aspect selection.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"el:readback — FieldState and SurfaceRealization types not yet "
|
||||
"finalized. When implemented: output must be fully inflected Koine Greek "
|
||||
"with correct accent placement, article resolution, and aspect selection."
|
||||
)
|
||||
def readback(field_state: object, intent: object = None) -> SurfaceRealization:
|
||||
return readback_from_intent(field_state, intent, language="el")
|
||||
|
|
|
|||
|
|
@ -1,92 +1,19 @@
|
|||
"""
|
||||
Validators for the Koine Greek depth pack.
|
||||
Same gate structure as en and he. Greek-specific gate notes inline.
|
||||
"""
|
||||
"""Executable validators for the Koine Greek articulation pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
import json
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from packs.common.validator import validate_pack_dir
|
||||
|
||||
PACK_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def _gate_schema() -> tuple[bool, str]:
|
||||
return False, "not yet wired"
|
||||
|
||||
|
||||
def _gate_lexical() -> tuple[bool, str]:
|
||||
seen = set()
|
||||
for line in (PACK_DIR / "lemmas.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
record = json.loads(line)
|
||||
lid = record["lemma_id"]
|
||||
if lid in seen:
|
||||
return False, f"duplicate lemma_id: {lid}"
|
||||
seen.add(lid)
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_morphology() -> tuple[bool, str]:
|
||||
known = set()
|
||||
for line in (PACK_DIR / "lemmas.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if line.strip():
|
||||
known.add(json.loads(line)["lemma_id"])
|
||||
for line in (PACK_DIR / "morphology.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
record = json.loads(line)
|
||||
if record["lemma_id"] not in known:
|
||||
return False, f"unknown lemma_id in morphology: {record['lemma_id']}"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_lift() -> tuple[bool, str]:
|
||||
# Greek lift requires tense-aspect-voice-mood and article resolution in analysis.
|
||||
return False, "el:lift() not yet implemented — requires tense/aspect/voice/mood/article in LinguisticAnalysis"
|
||||
|
||||
|
||||
def _gate_readback() -> tuple[bool, str]:
|
||||
return False, "el:readback() not yet implemented — must produce fully inflected and accented Greek"
|
||||
|
||||
|
||||
def _gate_determinism() -> tuple[bool, str]:
|
||||
return False, "depends on lift and readback"
|
||||
|
||||
|
||||
def _gate_alignment() -> tuple[bool, str]:
|
||||
return False, "anchors() not yet implemented"
|
||||
|
||||
|
||||
def _gate_coverage() -> tuple[bool, str]:
|
||||
return False, "depends on lift and readback gates"
|
||||
|
||||
|
||||
GATES = [
|
||||
("schema", _gate_schema),
|
||||
("lexical", _gate_lexical),
|
||||
("morphology", _gate_morphology),
|
||||
("lift", _gate_lift),
|
||||
("readback", _gate_readback),
|
||||
("determinism", _gate_determinism),
|
||||
("alignment", _gate_alignment),
|
||||
("coverage", _gate_coverage),
|
||||
]
|
||||
|
||||
|
||||
def validate_pack() -> dict:
|
||||
report = {"pack_id": "el", "active": False, "gates": {}}
|
||||
for name, gate_fn in GATES:
|
||||
passed, reason = gate_fn()
|
||||
report["gates"][name] = {"passed": passed, "reason": reason}
|
||||
if not passed:
|
||||
for remaining_name, _ in GATES[GATES.index((name, gate_fn)) + 1:]:
|
||||
report["gates"][remaining_name] = {"passed": False, "reason": "blocked by prior gate failure"}
|
||||
return report
|
||||
report["active"] = True
|
||||
return report
|
||||
return validate_pack_dir(PACK_DIR, pack_id="el", language="el")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pprint
|
||||
|
||||
pprint.pprint(validate_pack())
|
||||
|
|
|
|||
|
|
@ -1,45 +1,14 @@
|
|||
"""
|
||||
Lift rules for the English base pack.
|
||||
|
||||
Responsibility: receive a LinguisticAnalysis produced by the en pack normalizer
|
||||
and analyzer, and return a CandidatePressureBatch — a list of
|
||||
CandidateGeometricPressure packets ready for the IngestCompiler.
|
||||
|
||||
Design constraints:
|
||||
- Deterministic: identical input always produces identical output.
|
||||
- Lemma-first: lift targets are resolved through lemma_id → sense_id,
|
||||
not through surface-form heuristics.
|
||||
- Shared field target: every field_target must be a recognized CORE
|
||||
field primitive. No private semantic space.
|
||||
- This file must not import or invoke any external model or API.
|
||||
Lift is a deterministic, structure-driven operation.
|
||||
|
||||
Current status:
|
||||
The normalize() and analyze() interfaces are not yet fully specified
|
||||
for the en pack. Lift is blocked until those contracts are finalized
|
||||
and the LinguisticAnalysis type is stable.
|
||||
|
||||
Raise NotImplementedError at the exact boundary that is not yet designed
|
||||
rather than producing silent or approximate output.
|
||||
"""
|
||||
"""Deterministic lift rules for the English base pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core_ingest.pressure import CandidateGeometricPressure
|
||||
from pathlib import Path
|
||||
|
||||
from core_ingest.types import CandidateGeometricPressure
|
||||
from packs.common.runtime_rules import lift_from_pack
|
||||
|
||||
PACK_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def lift(analysis: object) -> list["CandidateGeometricPressure"]:
|
||||
"""
|
||||
Lift a LinguisticAnalysis from the en pack into a list of
|
||||
CandidateGeometricPressure packets.
|
||||
|
||||
Blocked on: finalization of the LinguisticAnalysis contract
|
||||
and the CandidateGeometricPressure construction interface.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"en:lift — LinguisticAnalysis contract not yet finalized. "
|
||||
"Implement after analyze() and CandidateGeometricPressure "
|
||||
"construction interface are locked."
|
||||
)
|
||||
def lift(analysis: object) -> list[CandidateGeometricPressure]:
|
||||
return lift_from_pack(PACK_DIR, analysis, language="en")
|
||||
|
|
|
|||
|
|
@ -1,37 +1,9 @@
|
|||
"""
|
||||
Readback rules for the English base pack.
|
||||
|
||||
Responsibility: receive a resolved field state (or a field state fragment
|
||||
with a stated communicative intent) and produce a grammatical English
|
||||
surface realization.
|
||||
|
||||
Design constraints:
|
||||
- Readback is pack-local. This file does not reach into he or el rules.
|
||||
- Grammatical agreement is fully handled here: number, tense, mood.
|
||||
- Ambiguity resolution is deterministic: when multiple lemmas could
|
||||
express the same field target, rank and constraint matching decide.
|
||||
- This file must not invoke any external model or API.
|
||||
|
||||
Current status:
|
||||
Readback requires the FieldState type and the SurfaceRealization
|
||||
return type to be stable. Both are blocked on the field primitive
|
||||
specification work in the core field layer.
|
||||
"""
|
||||
"""Deterministic readback rules for the English base pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
from packs.common.runtime_rules import SurfaceRealization, readback_from_intent
|
||||
|
||||
|
||||
def readback(field_state: object, intent: object = None) -> object:
|
||||
"""
|
||||
Produce a grammatical English surface realization from a field state.
|
||||
|
||||
Blocked on: FieldState type and SurfaceRealization interface.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"en:readback — FieldState and SurfaceRealization types not yet "
|
||||
"finalized. Implement after the core field primitive layer is locked."
|
||||
)
|
||||
def readback(field_state: object, intent: object = None) -> SurfaceRealization:
|
||||
return readback_from_intent(field_state, intent, language="en")
|
||||
|
|
|
|||
|
|
@ -1,108 +1,19 @@
|
|||
"""
|
||||
Validators for the English base pack.
|
||||
|
||||
Runs all eight gates in sequence and returns a ValidationReport.
|
||||
A gate failure halts further validation — the pack is not partially active.
|
||||
|
||||
Gate status reflects current implementation reality.
|
||||
Do not mark a gate True until it passes programmatically.
|
||||
"""
|
||||
"""Executable validators for the English base pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
import json
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from packs.common.validator import validate_pack_dir
|
||||
|
||||
PACK_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def _gate_schema() -> tuple[bool, str]:
|
||||
"""Validate all .jsonl files against their JSON Schema counterparts."""
|
||||
# Requires: jsonschema library and schema files under packs/common/schema/
|
||||
# Status: schema files exist; validation runner not yet wired.
|
||||
return False, "not yet wired"
|
||||
|
||||
|
||||
def _gate_lexical() -> tuple[bool, str]:
|
||||
"""Check lemma_id uniqueness and field_hook validity."""
|
||||
seen = set()
|
||||
for line in (PACK_DIR / "lemmas.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
record = json.loads(line)
|
||||
lid = record["lemma_id"]
|
||||
if lid in seen:
|
||||
return False, f"duplicate lemma_id: {lid}"
|
||||
seen.add(lid)
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_morphology() -> tuple[bool, str]:
|
||||
"""Check all morphology records reference a known lemma_id."""
|
||||
known = set()
|
||||
for line in (PACK_DIR / "lemmas.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if line.strip():
|
||||
known.add(json.loads(line)["lemma_id"])
|
||||
for line in (PACK_DIR / "morphology.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
record = json.loads(line)
|
||||
if record["lemma_id"] not in known:
|
||||
return False, f"unknown lemma_id in morphology: {record['lemma_id']}"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_lift() -> tuple[bool, str]:
|
||||
"""Run lift probes. Blocked on lift() implementation."""
|
||||
return False, "lift() not yet implemented"
|
||||
|
||||
|
||||
def _gate_readback() -> tuple[bool, str]:
|
||||
"""Run readback probes. Blocked on readback() implementation."""
|
||||
return False, "readback() not yet implemented"
|
||||
|
||||
|
||||
def _gate_determinism() -> tuple[bool, str]:
|
||||
"""Verify normalize() and lift() are deterministic. Blocked on both."""
|
||||
return False, "normalize() and lift() not yet implemented"
|
||||
|
||||
|
||||
def _gate_alignment() -> tuple[bool, str]:
|
||||
"""Check anchors() returns the required trilingual anchor set."""
|
||||
return False, "anchors() not yet implemented"
|
||||
|
||||
|
||||
def _gate_coverage() -> tuple[bool, str]:
|
||||
"""Run all probes in probes/. Blocked on lift and readback."""
|
||||
return False, "depends on lift and readback gates"
|
||||
|
||||
|
||||
GATES = [
|
||||
("schema", _gate_schema),
|
||||
("lexical", _gate_lexical),
|
||||
("morphology", _gate_morphology),
|
||||
("lift", _gate_lift),
|
||||
("readback", _gate_readback),
|
||||
("determinism", _gate_determinism),
|
||||
("alignment", _gate_alignment),
|
||||
("coverage", _gate_coverage),
|
||||
]
|
||||
|
||||
|
||||
def validate_pack() -> dict:
|
||||
"""Run all eight gates. Returns a report with pass/fail and reason per gate."""
|
||||
report = {"pack_id": "en", "active": False, "gates": {}}
|
||||
for name, gate_fn in GATES:
|
||||
passed, reason = gate_fn()
|
||||
report["gates"][name] = {"passed": passed, "reason": reason}
|
||||
if not passed:
|
||||
# Gate failure halts further validation.
|
||||
for remaining_name, _ in GATES[GATES.index((name, gate_fn)) + 1:]:
|
||||
report["gates"][remaining_name] = {"passed": False, "reason": "blocked by prior gate failure"}
|
||||
return report
|
||||
report["active"] = True
|
||||
return report
|
||||
return validate_pack_dir(PACK_DIR, pack_id="en", language="en")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pprint
|
||||
|
||||
pprint.pprint(validate_pack())
|
||||
|
|
|
|||
|
|
@ -1,42 +1,14 @@
|
|||
"""
|
||||
Lift rules for the Koine Greek depth pack.
|
||||
|
||||
Responsibility: receive a LinguisticAnalysis from the el pack analyzer
|
||||
and return a CandidatePressureBatch.
|
||||
|
||||
Koine Greek-specific lift requirements:
|
||||
- Tense-aspect: Greek tense encodes both time and aspect. The imperfect
|
||||
of eimi (en) lifts into existence.state.continuous, not existence.state.
|
||||
The aorist would lift into existence.event. These are not interchangeable.
|
||||
- Article system: the presence or absence of the article on a predicate
|
||||
nominative is semantically load-bearing (Colwell construction).
|
||||
Anarthrous predicate nominatives must lift with a qualitative tag.
|
||||
- Voice: middle voice is not passive. Middle voice lifts carry a
|
||||
reflexive or self-involving semantic that must be preserved.
|
||||
- Pros with accusative: lifts into relation.presence-toward,
|
||||
not relation.accompaniment. The preposition selects the sense.
|
||||
|
||||
Current status:
|
||||
Blocked on LinguisticAnalysis contract (el pack specific: must carry
|
||||
full tense-aspect-voice-mood bundle and article resolution).
|
||||
"""
|
||||
"""Deterministic lift rules for the Koine Greek depth pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core_ingest.pressure import CandidateGeometricPressure
|
||||
from pathlib import Path
|
||||
|
||||
from core_ingest.types import CandidateGeometricPressure
|
||||
from packs.common.runtime_rules import lift_from_pack
|
||||
|
||||
PACK_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def lift(analysis: object) -> list["CandidateGeometricPressure"]:
|
||||
"""
|
||||
Lift a Greek LinguisticAnalysis into CandidateGeometricPressure packets.
|
||||
|
||||
Blocked on: el pack LinguisticAnalysis contract — must carry
|
||||
tense-aspect-voice-mood bundle and article resolution (arthrous/anarthrous)
|
||||
before this can be implemented correctly.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"el:lift — LinguisticAnalysis contract for Koine Greek not yet finalized. "
|
||||
"Must carry tense, aspect, voice, mood, and article resolution."
|
||||
)
|
||||
def lift(analysis: object) -> list[CandidateGeometricPressure]:
|
||||
return lift_from_pack(PACK_DIR, analysis, language="grc")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{"record_id":"el:logos:nominative-sg","lemma_id":"el:logos","surface_form":"λόγος","features":{"case":"nominative","number":"sg","gender":"m"},"notes":"Nominative subject form. Used in John 1:1a and 1:1c."}
|
||||
{"record_id":"el:logos:nominative-sg-anarthrous","lemma_id":"el:logos","surface_form":"λόγος","features":{"case":"nominative","number":"sg","gender":"m","arthrous":false},"notes":"Anarthrous nominative. In John 1:1c θεὸς ἦν ὁ λόγος the predicate theos is anarthrous, marking quality not identity. This is the Colwell construction."}
|
||||
{"record_id":"el:eimi:imperfect:3sg","lemma_id":"el:eimi","surface_form":"ἦν","features":{"tense":"imperfect","voice":"active","mood":"indicative","person":"3","number":"sg"},"notes":"John 1:1 en arche en ho logos. The imperfect signals continuous, durative being in the past — the Logos was already in existence at the beginning, not that he came into being then."}
|
||||
{"record_id":"el:eimi:present:1sg","lemma_id":"el:eimi","surface_form":"εἰμί","features":{"tense":"present","voice":"active","mood":"indicative","person":"1","number":"sg"},"notes":null}
|
||||
{"record_id":"el:arche:dative-sg","lemma_id":"el:arche","surface_form":"ἀρχῇ","features":{"case":"dative","number":"sg"},"notes":"John 1:1 en arche. Dative of sphere or reference. The Logos existed within/at the beginning as its defining point."}
|
||||
{"record_id":"el:theos:nominative-sg-anarthrous","lemma_id":"el:theos","surface_form":"θεός","features":{"case":"nominative","number":"sg","arthrous":false},"notes":"John 1:1c predicate nominative. Anarthrous: marks divine nature/quality of the Logos, not mere identity equation."}
|
||||
{"record_id":"el:pros:accusative-gov","lemma_id":"el:pros","surface_form":"πρός","features":{"governs":"accusative"},"notes":"pros with accusative in John 1:1b: pros ton theon. Signals directional presence-toward, intimate relational orientation, not mere proximity."}
|
||||
{"record_id":"grc:logos:nominative-sg","lemma_id":"grc:logos","surface_form":"λόγος","features":{"case":"nominative","number":"sg","gender":"m"},"notes":"Nominative subject form. Used in John 1:1a and 1:1c."}
|
||||
{"record_id":"grc:logos:nominative-sg-anarthrous","lemma_id":"grc:logos","surface_form":"λόγος","features":{"case":"nominative","number":"sg","gender":"m","arthrous":false},"notes":"Anarthrous nominative. In John 1:1c θεὸς ἦν ὁ λόγος the predicate theos is anarthrous, marking quality not identity. This is the Colwell construction."}
|
||||
{"record_id":"grc:eimi:imperfect:3sg","lemma_id":"grc:eimi","surface_form":"ἦν","features":{"tense":"imperfect","voice":"active","mood":"indicative","person":"3","number":"sg"},"notes":"John 1:1 en arche en ho logos. The imperfect signals continuous, durative being in the past — the Logos was already in existence at the beginning, not that he came into being then."}
|
||||
{"record_id":"grc:eimi:present:1sg","lemma_id":"grc:eimi","surface_form":"εἰμί","features":{"tense":"present","voice":"active","mood":"indicative","person":"1","number":"sg"},"notes":null}
|
||||
{"record_id":"grc:arche:dative-sg","lemma_id":"grc:arche","surface_form":"ἀρχῇ","features":{"case":"dative","number":"sg"},"notes":"John 1:1 en arche. Dative of sphere or reference. The Logos existed within/at the beginning as its defining point."}
|
||||
{"record_id":"grc:theos:nominative-sg-anarthrous","lemma_id":"grc:theos","surface_form":"θεός","features":{"case":"nominative","number":"sg","arthrous":false},"notes":"John 1:1c predicate nominative. Anarthrous: marks divine nature/quality of the Logos, not mere identity equation."}
|
||||
{"record_id":"grc:pros:accusative-gov","lemma_id":"grc:pros","surface_form":"πρός","features":{"governs":"accusative"},"notes":"pros with accusative in John 1:1b: pros ton theon. Signals directional presence-toward, intimate relational orientation, not mere proximity."}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{"probe_id":"el:normalize:john-1-1","kind":"normalize","input":{"text":"Ἐν ἀρχῇ ἦν ὁ λόγος"},"expected":{"normalized":"Ἐν ἀρχῇ ἦν ὁ λόγος","unicode_form":"NFC","breathing_marks_preserved":true,"accents_preserved":true},"tolerance":null}
|
||||
{"probe_id":"el:lift:logos-creative","kind":"lift","input":{"lemma_id":"el:logos","sense_id":"el:logos:creative-word","frame_id":"el:copular-basic"},"expected":{"field_target":"logos.articulation.creative","pressure_kind":"semantic"},"tolerance":null}
|
||||
{"probe_id":"el:lift:eimi-continuous","kind":"lift","input":{"lemma_id":"el:eimi","sense_id":"el:eimi:continuous-being","frame_id":"el:existential-imperfect"},"expected":{"field_target":"existence.state.continuous"},"tolerance":null}
|
||||
{"probe_id":"el:alignment:logos-anchor","kind":"alignment","input":{"anchor_id":"logos-word-creative-speech","lang":"el"},"expected":{"lemma_id":"el:logos","sense_id":"el:logos:creative-word","field_target":"logos.articulation.creative"},"tolerance":null}
|
||||
{"probe_id":"el:alignment:beginning-anchor","kind":"alignment","input":{"anchor_id":"beginning-origin-temporal-absolute","lang":"el"},"expected":{"lemma_id":"el:arche","sense_id":"el:arche:absolute-origin","field_target":"time.origin.absolute"},"tolerance":null}
|
||||
{"probe_id":"el:alignment:existence-anchor","kind":"alignment","input":{"anchor_id":"existence-being-copular","lang":"el"},"expected":{"lemma_id":"el:eimi","sense_id":"el:eimi:existence","field_target":"existence.state.identity"},"tolerance":null}
|
||||
{"probe_id":"grc:normalize:john-1-1","kind":"normalize","input":{"text":"Ἐν ἀρχῇ ἦν ὁ λόγος"},"expected":{"normalized":"Ἐν ἀρχῇ ἦν ὁ λόγος","unicode_form":"NFC","breathing_marks_preserved":true,"accents_preserved":true},"tolerance":null}
|
||||
{"probe_id":"grc:lift:logos-creative","kind":"lift","input":{"lemma_id":"grc:logos","sense_id":"grc:logos:creative-word","frame_id":"grc:copular-basic"},"expected":{"field_target":"logos.articulation.creative","pressure_kind":"semantic"},"tolerance":null}
|
||||
{"probe_id":"grc:lift:eimi-continuous","kind":"lift","input":{"lemma_id":"grc:eimi","sense_id":"grc:eimi:continuous-being","frame_id":"grc:existential-imperfect"},"expected":{"field_target":"existence.state.continuous"},"tolerance":null}
|
||||
{"probe_id":"grc:alignment:logos-anchor","kind":"alignment","input":{"anchor_id":"logos-word-creative-speech","lang":"grc"},"expected":{"lemma_id":"grc:logos","sense_id":"grc:logos:creative-word","field_target":"logos.articulation.creative"},"tolerance":null}
|
||||
{"probe_id":"grc:alignment:beginning-anchor","kind":"alignment","input":{"anchor_id":"beginning-origin-temporal-absolute","lang":"grc"},"expected":{"lemma_id":"grc:arche","sense_id":"grc:arche:absolute-origin","field_target":"time.origin.absolute"},"tolerance":null}
|
||||
{"probe_id":"grc:alignment:existence-anchor","kind":"alignment","input":{"anchor_id":"existence-being-copular","lang":"grc"},"expected":{"lemma_id":"grc:eimi","sense_id":"grc:eimi:existence","field_target":"existence.state.identity"},"tolerance":null}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,9 @@
|
|||
"""
|
||||
Readback rules for the Koine Greek depth pack.
|
||||
|
||||
Koine Greek readback produces grammatical Biblical Greek surface realizations.
|
||||
This is a depth-pack operation invoked only when the model targets Greek articulation.
|
||||
|
||||
Koine Greek-specific readback requirements:
|
||||
- Full inflection: case, number, gender on nouns/adjectives/articles;
|
||||
tense, voice, mood, person, number on verbs. Every word fully inflected.
|
||||
- Accent placement: Greek accents must be placed correctly.
|
||||
Unaccented output is not a valid surface realization.
|
||||
- Article resolution: the article must be present or absent based on
|
||||
the semantic role of the noun in the clause. Anarthrous predicates
|
||||
in copular constructions must remain anarthrous (Colwell).
|
||||
- Aspect selection: aorist vs. imperfect vs. perfect is not stylistic.
|
||||
Each carries distinct semantic content that must be honored.
|
||||
|
||||
Current status:
|
||||
Blocked on FieldState and SurfaceRealization types.
|
||||
"""
|
||||
"""Deterministic readback rules for the Koine Greek depth pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from packs.common.runtime_rules import SurfaceRealization, readback_from_intent
|
||||
|
||||
def readback(field_state: object, intent: object = None) -> object:
|
||||
"""
|
||||
Produce a grammatical Koine Greek surface realization from a field state.
|
||||
|
||||
Blocked on: FieldState and SurfaceRealization types.
|
||||
When implemented: must produce fully inflected and accented Greek output
|
||||
with correct article placement and tense-aspect selection.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"el:readback — FieldState and SurfaceRealization types not yet "
|
||||
"finalized. When implemented: output must be fully inflected Koine Greek "
|
||||
"with correct accent placement, article resolution, and aspect selection."
|
||||
)
|
||||
def readback(field_state: object, intent: object = None) -> SurfaceRealization:
|
||||
return readback_from_intent(field_state, intent, language="grc")
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{"sense_id":"el:logos:creative-word","lemma_id":"el:logos","field_target":"logos.articulation.creative","constraints":["creative-or-authoritative-context"],"rank":1}
|
||||
{"sense_id":"el:logos:reason","lemma_id":"el:logos","field_target":"logos.reason","constraints":["philosophical-context"],"rank":2}
|
||||
{"sense_id":"el:logos:person","lemma_id":"el:logos","field_target":"logos.person","constraints":["johannine-context"],"rank":3}
|
||||
{"sense_id":"el:eimi:existence","lemma_id":"el:eimi","field_target":"existence.state.identity","constraints":["copular-frame"],"rank":1}
|
||||
{"sense_id":"el:eimi:continuous-being","lemma_id":"el:eimi","field_target":"existence.state.continuous","constraints":["existential-imperfect-frame"],"rank":2}
|
||||
{"sense_id":"el:arche:absolute-origin","lemma_id":"el:arche","field_target":"time.origin.absolute","constraints":["absolute-temporal-or-ontological-context"],"rank":1}
|
||||
{"sense_id":"el:arche:first-principle","lemma_id":"el:arche","field_target":"logos.first-principle","constraints":["philosophical-context"],"rank":2}
|
||||
{"sense_id":"el:theos:divine-identity","lemma_id":"el:theos","field_target":"divine.identity","constraints":[],"rank":1}
|
||||
{"sense_id":"el:pros:presence-toward","lemma_id":"el:pros","field_target":"relation.presence-toward","constraints":["pros-governs-accusative"],"rank":1}
|
||||
{"sense_id":"grc:logos:creative-word","lemma_id":"grc:logos","field_target":"logos.articulation.creative","constraints":["creative-or-authoritative-context"],"rank":1}
|
||||
{"sense_id":"grc:logos:reason","lemma_id":"grc:logos","field_target":"logos.reason","constraints":["philosophical-context"],"rank":2}
|
||||
{"sense_id":"grc:logos:person","lemma_id":"grc:logos","field_target":"logos.person","constraints":["johannine-context"],"rank":3}
|
||||
{"sense_id":"grc:eimi:existence","lemma_id":"grc:eimi","field_target":"existence.state.identity","constraints":["copular-frame"],"rank":1}
|
||||
{"sense_id":"grc:eimi:continuous-being","lemma_id":"grc:eimi","field_target":"existence.state.continuous","constraints":["existential-imperfect-frame"],"rank":2}
|
||||
{"sense_id":"grc:arche:absolute-origin","lemma_id":"grc:arche","field_target":"time.origin.absolute","constraints":["absolute-temporal-or-ontological-context"],"rank":1}
|
||||
{"sense_id":"grc:arche:first-principle","lemma_id":"grc:arche","field_target":"logos.first-principle","constraints":["philosophical-context"],"rank":2}
|
||||
{"sense_id":"grc:theos:divine-identity","lemma_id":"grc:theos","field_target":"divine.identity","constraints":[],"rank":1}
|
||||
{"sense_id":"grc:pros:presence-toward","lemma_id":"grc:pros","field_target":"relation.presence-toward","constraints":["pros-governs-accusative"],"rank":1}
|
||||
|
|
|
|||
|
|
@ -1,92 +1,19 @@
|
|||
"""
|
||||
Validators for the Koine Greek depth pack.
|
||||
Same gate structure as en and he. Greek-specific gate notes inline.
|
||||
"""
|
||||
"""Executable validators for the Koine Greek depth pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
import json
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from packs.common.validator import validate_pack_dir
|
||||
|
||||
PACK_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def _gate_schema() -> tuple[bool, str]:
|
||||
return False, "not yet wired"
|
||||
|
||||
|
||||
def _gate_lexical() -> tuple[bool, str]:
|
||||
seen = set()
|
||||
for line in (PACK_DIR / "lemmas.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
record = json.loads(line)
|
||||
lid = record["lemma_id"]
|
||||
if lid in seen:
|
||||
return False, f"duplicate lemma_id: {lid}"
|
||||
seen.add(lid)
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_morphology() -> tuple[bool, str]:
|
||||
known = set()
|
||||
for line in (PACK_DIR / "lemmas.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if line.strip():
|
||||
known.add(json.loads(line)["lemma_id"])
|
||||
for line in (PACK_DIR / "morphology.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
record = json.loads(line)
|
||||
if record["lemma_id"] not in known:
|
||||
return False, f"unknown lemma_id in morphology: {record['lemma_id']}"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_lift() -> tuple[bool, str]:
|
||||
# Greek lift requires tense-aspect-voice-mood and article resolution in analysis.
|
||||
return False, "el:lift() not yet implemented — requires tense/aspect/voice/mood/article in LinguisticAnalysis"
|
||||
|
||||
|
||||
def _gate_readback() -> tuple[bool, str]:
|
||||
return False, "el:readback() not yet implemented — must produce fully inflected and accented Greek"
|
||||
|
||||
|
||||
def _gate_determinism() -> tuple[bool, str]:
|
||||
return False, "depends on lift and readback"
|
||||
|
||||
|
||||
def _gate_alignment() -> tuple[bool, str]:
|
||||
return False, "anchors() not yet implemented"
|
||||
|
||||
|
||||
def _gate_coverage() -> tuple[bool, str]:
|
||||
return False, "depends on lift and readback gates"
|
||||
|
||||
|
||||
GATES = [
|
||||
("schema", _gate_schema),
|
||||
("lexical", _gate_lexical),
|
||||
("morphology", _gate_morphology),
|
||||
("lift", _gate_lift),
|
||||
("readback", _gate_readback),
|
||||
("determinism", _gate_determinism),
|
||||
("alignment", _gate_alignment),
|
||||
("coverage", _gate_coverage),
|
||||
]
|
||||
|
||||
|
||||
def validate_pack() -> dict:
|
||||
report = {"pack_id": "el", "active": False, "gates": {}}
|
||||
for name, gate_fn in GATES:
|
||||
passed, reason = gate_fn()
|
||||
report["gates"][name] = {"passed": passed, "reason": reason}
|
||||
if not passed:
|
||||
for remaining_name, _ in GATES[GATES.index((name, gate_fn)) + 1:]:
|
||||
report["gates"][remaining_name] = {"passed": False, "reason": "blocked by prior gate failure"}
|
||||
return report
|
||||
report["active"] = True
|
||||
return report
|
||||
return validate_pack_dir(PACK_DIR, pack_id="grc", language="grc")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pprint
|
||||
|
||||
pprint.pprint(validate_pack())
|
||||
|
|
|
|||
|
|
@ -1,42 +1,14 @@
|
|||
"""
|
||||
Lift rules for the Hebrew depth pack.
|
||||
|
||||
Responsibility: receive a LinguisticAnalysis from the he pack analyzer
|
||||
and return a CandidatePressureBatch.
|
||||
|
||||
Hebrew-specific lift requirements:
|
||||
- Binyan (verb stem) must be resolved before field_target is selected.
|
||||
The same root in qal vs. hiphil may lift into different field targets.
|
||||
- Aspect (qatal/yiqtol/wayyiqtol) contributes to the pressure kind
|
||||
and temporal annotation of the candidate.
|
||||
- Construct chains must be handled as relational frames: the head
|
||||
lemma and the genitive together determine the lift target.
|
||||
- The implicit copula: when haya is absent, the copular frame is
|
||||
inferred from syntactic position, not from a present verb form.
|
||||
- bara in qal: always lifts into creation.act.ex-nihilo.
|
||||
The divine-agent constraint must be verified before this lift.
|
||||
|
||||
Current status:
|
||||
Blocked on LinguisticAnalysis contract (he pack specific: must carry
|
||||
binyan, aspect, and construct-chain annotations).
|
||||
"""
|
||||
"""Deterministic lift rules for the Hebrew depth pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core_ingest.pressure import CandidateGeometricPressure
|
||||
from pathlib import Path
|
||||
|
||||
from core_ingest.types import CandidateGeometricPressure
|
||||
from packs.common.runtime_rules import lift_from_pack
|
||||
|
||||
PACK_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def lift(analysis: object) -> list["CandidateGeometricPressure"]:
|
||||
"""
|
||||
Lift a Hebrew LinguisticAnalysis into CandidateGeometricPressure packets.
|
||||
|
||||
Blocked on: he pack LinguisticAnalysis contract — must carry
|
||||
binyan, aspect, and construct-chain annotations before this
|
||||
can be implemented correctly.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"he:lift — LinguisticAnalysis contract for Hebrew not yet finalized. "
|
||||
"Must carry binyan, aspect, and construct-chain annotations."
|
||||
)
|
||||
def lift(analysis: object) -> list[CandidateGeometricPressure]:
|
||||
return lift_from_pack(PACK_DIR, analysis, language="he")
|
||||
|
|
|
|||
|
|
@ -1,38 +1,9 @@
|
|||
"""
|
||||
Readback rules for the Hebrew depth pack.
|
||||
|
||||
Hebrew readback produces grammatical Biblical Hebrew surface realizations
|
||||
from field state. This is a depth-pack operation: it is not invoked
|
||||
by default, only when the model explicitly targets Hebrew articulation.
|
||||
|
||||
Hebrew-specific readback requirements:
|
||||
- Verb selection must respect binyan: the field target and agent
|
||||
semantics together determine which binyan is appropriate.
|
||||
- Aspect selection is not optional: qatal, yiqtol, and wayyiqtol
|
||||
carry distinct temporal and narrative semantics.
|
||||
- Nikud (vowel points) must be produced, not left unpointed.
|
||||
Unpointed output is not a valid surface realization for this pack.
|
||||
- Construct chains must be assembled correctly: head in construct
|
||||
state, genitive following, no article on the construct head.
|
||||
- The implicit copula must be handled: in nominal clauses, haya
|
||||
is omitted in the present tense unless emphasis requires it.
|
||||
|
||||
Current status:
|
||||
Blocked on FieldState and SurfaceRealization types.
|
||||
"""
|
||||
"""Deterministic readback rules for the Hebrew depth pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from packs.common.runtime_rules import SurfaceRealization, readback_from_intent
|
||||
|
||||
def readback(field_state: object, intent: object = None) -> object:
|
||||
"""
|
||||
Produce a grammatical Hebrew surface realization from a field state.
|
||||
|
||||
Blocked on: FieldState and SurfaceRealization types.
|
||||
When implemented: must produce fully pointed (nikud) Hebrew output.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"he:readback — FieldState and SurfaceRealization types not yet "
|
||||
"finalized. When implemented: output must be fully pointed Hebrew "
|
||||
"with correct binyan, aspect, and construct-chain handling."
|
||||
)
|
||||
def readback(field_state: object, intent: object = None) -> SurfaceRealization:
|
||||
return readback_from_intent(field_state, intent, language="he")
|
||||
|
|
|
|||
|
|
@ -1,93 +1,19 @@
|
|||
"""
|
||||
Validators for the Hebrew depth pack.
|
||||
Same gate structure as the en pack. Hebrew-specific gate notes inline.
|
||||
"""
|
||||
"""Executable validators for the Hebrew depth pack."""
|
||||
|
||||
from __future__ import annotations
|
||||
import json
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from packs.common.validator import validate_pack_dir
|
||||
|
||||
PACK_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def _gate_schema() -> tuple[bool, str]:
|
||||
return False, "not yet wired"
|
||||
|
||||
|
||||
def _gate_lexical() -> tuple[bool, str]:
|
||||
seen = set()
|
||||
for line in (PACK_DIR / "lemmas.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
record = json.loads(line)
|
||||
lid = record["lemma_id"]
|
||||
if lid in seen:
|
||||
return False, f"duplicate lemma_id: {lid}"
|
||||
seen.add(lid)
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_morphology() -> tuple[bool, str]:
|
||||
known = set()
|
||||
for line in (PACK_DIR / "lemmas.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if line.strip():
|
||||
known.add(json.loads(line)["lemma_id"])
|
||||
for line in (PACK_DIR / "morphology.jsonl").read_text(encoding="utf-8").splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
record = json.loads(line)
|
||||
if record["lemma_id"] not in known:
|
||||
return False, f"unknown lemma_id in morphology: {record['lemma_id']}"
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def _gate_lift() -> tuple[bool, str]:
|
||||
# Hebrew lift additionally requires binyan and aspect annotations
|
||||
# in the LinguisticAnalysis. These are not yet specified.
|
||||
return False, "he:lift() not yet implemented — requires binyan/aspect in LinguisticAnalysis"
|
||||
|
||||
|
||||
def _gate_readback() -> tuple[bool, str]:
|
||||
return False, "he:readback() not yet implemented — must produce pointed Hebrew output"
|
||||
|
||||
|
||||
def _gate_determinism() -> tuple[bool, str]:
|
||||
return False, "depends on lift and readback"
|
||||
|
||||
|
||||
def _gate_alignment() -> tuple[bool, str]:
|
||||
return False, "anchors() not yet implemented"
|
||||
|
||||
|
||||
def _gate_coverage() -> tuple[bool, str]:
|
||||
return False, "depends on lift and readback gates"
|
||||
|
||||
|
||||
GATES = [
|
||||
("schema", _gate_schema),
|
||||
("lexical", _gate_lexical),
|
||||
("morphology", _gate_morphology),
|
||||
("lift", _gate_lift),
|
||||
("readback", _gate_readback),
|
||||
("determinism", _gate_determinism),
|
||||
("alignment", _gate_alignment),
|
||||
("coverage", _gate_coverage),
|
||||
]
|
||||
|
||||
|
||||
def validate_pack() -> dict:
|
||||
report = {"pack_id": "he", "active": False, "gates": {}}
|
||||
for name, gate_fn in GATES:
|
||||
passed, reason = gate_fn()
|
||||
report["gates"][name] = {"passed": passed, "reason": reason}
|
||||
if not passed:
|
||||
for remaining_name, _ in GATES[GATES.index((name, gate_fn)) + 1:]:
|
||||
report["gates"][remaining_name] = {"passed": False, "reason": "blocked by prior gate failure"}
|
||||
return report
|
||||
report["active"] = True
|
||||
return report
|
||||
return validate_pack_dir(PACK_DIR, pack_id="he", language="he")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pprint
|
||||
|
||||
pprint.pprint(validate_pack())
|
||||
|
|
|
|||
|
|
@ -101,6 +101,32 @@ class TextProjectionHead:
|
|||
return False
|
||||
|
||||
|
||||
class TextSurfaceDecoder:
|
||||
"""Exact text reconstruction over the mounted modality vocabulary."""
|
||||
|
||||
modality: Modality = Modality.TEXT
|
||||
|
||||
def __init__(self, vocabulary: ModalityVocabulary) -> None:
|
||||
self._vocab = vocabulary
|
||||
|
||||
def decode(self, mv: np.ndarray) -> str:
|
||||
query = np.asarray(mv, dtype=np.float32)
|
||||
best_token: str | None = None
|
||||
best_distance = float("inf")
|
||||
for token in self._vocab._point_keys:
|
||||
point = self._vocab.get_point(token)
|
||||
distance = float(np.linalg.norm(query - point))
|
||||
if distance < best_distance:
|
||||
best_distance = distance
|
||||
best_token = str(token)
|
||||
if best_token is None:
|
||||
raise ValueError("cannot decode from an empty text vocabulary")
|
||||
return best_token
|
||||
|
||||
def decode_batch(self, mvs: np.ndarray) -> list[str]:
|
||||
return [self.decode(mv) for mv in np.asarray(mvs, dtype=np.float32)]
|
||||
|
||||
|
||||
def make_text_pack(
|
||||
pack_id: str,
|
||||
vocabulary: ModalityVocabulary | None = None,
|
||||
|
|
@ -124,13 +150,14 @@ def make_text_pack(
|
|||
"""
|
||||
vocab = vocabulary if vocabulary is not None else ModalityVocabulary()
|
||||
head = TextProjectionHead(vocab, oov_policy=oov_policy)
|
||||
decoder = TextSurfaceDecoder(vocab)
|
||||
return ModalityPack(
|
||||
pack_id=pack_id,
|
||||
modality_type=Modality.TEXT,
|
||||
projection=head,
|
||||
decoder=None, # text decode not yet implemented
|
||||
decoder=decoder,
|
||||
vocabulary=vocab,
|
||||
grammar_scaffold=None, # populated during Supervised Seeding Epoch
|
||||
grammar_scaffold=None,
|
||||
checksum_verified=checksum_verified,
|
||||
gate_engaged=gate_engaged,
|
||||
language_role=language_role,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ class SessionContext:
|
|||
node=node_idx,
|
||||
step=injected.step,
|
||||
holonomy=injected.holonomy,
|
||||
energy=injected.energy,
|
||||
valence=injected.valence,
|
||||
)
|
||||
else:
|
||||
self.state = FieldState(
|
||||
|
|
@ -53,6 +55,8 @@ class SessionContext:
|
|||
node=node_idx,
|
||||
step=self.state.step + 1,
|
||||
holonomy=injected.holonomy,
|
||||
energy=injected.energy,
|
||||
valence=injected.valence,
|
||||
)
|
||||
self.vault.store(self.state.F, {"turn": self.turn, "role": "user"})
|
||||
return self.state
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import numpy as np
|
|||
from algebra.backend import cga_inner
|
||||
from algebra.cl41 import geometric_product, reverse
|
||||
from algebra.versor import versor_unit_residual
|
||||
from core.physics.energy import EnergyProfile
|
||||
from core.physics.valence import ValenceBundle
|
||||
from language_packs.schema import MorphologyEntry
|
||||
|
||||
_MANIFOLD_RESIDUAL_TOLERANCE = 1e-5
|
||||
|
|
@ -68,6 +70,8 @@ class VocabManifold:
|
|||
self._versors: list[np.ndarray] = [] # each shape (32,), unit-versor ±1
|
||||
self._morphology_by_word: dict[str, MorphologyEntry] = {}
|
||||
self._language_by_word: dict[str, str] = {}
|
||||
self._energy_by_word: dict[str, EnergyProfile] = {}
|
||||
self._valence_by_word: dict[str, ValenceBundle] = {}
|
||||
self._transient_words: set[str] = set()
|
||||
self._unknown_token_log: list[dict[str, object]] = []
|
||||
|
||||
|
|
@ -77,6 +81,8 @@ class VocabManifold:
|
|||
versor: np.ndarray,
|
||||
morphology: MorphologyEntry | None = None,
|
||||
language: str | None = None,
|
||||
energy: EnergyProfile | None = None,
|
||||
valence: ValenceBundle | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Register a word-versor pair.
|
||||
|
|
@ -103,6 +109,10 @@ class VocabManifold:
|
|||
self._language_by_word[word] = resolved_language
|
||||
if morphology is not None:
|
||||
self._morphology_by_word[word] = morphology
|
||||
if energy is not None:
|
||||
self._energy_by_word[word] = energy
|
||||
if valence is not None:
|
||||
self._valence_by_word[word] = valence
|
||||
|
||||
def insert_transient(self, word: str, versor: np.ndarray) -> None:
|
||||
"""
|
||||
|
|
@ -203,6 +213,14 @@ class VocabManifold:
|
|||
"""Return structured morphology for a stored surface, if the pack provided it."""
|
||||
return self._morphology_by_word.get(word)
|
||||
|
||||
def energy_for_word(self, word: str) -> EnergyProfile | None:
|
||||
"""Return ADR-0006 energy profile for a stored surface, when available."""
|
||||
return self._energy_by_word.get(word)
|
||||
|
||||
def valence_for_word(self, word: str) -> ValenceBundle | None:
|
||||
"""Return ADR-0007 valence bundle for a stored surface, when available."""
|
||||
return self._valence_by_word.get(word)
|
||||
|
||||
def language_for_word(self, word: str) -> str | None:
|
||||
"""Return the language code for a stored surface, if known."""
|
||||
morphology = self._morphology_by_word.get(word)
|
||||
|
|
|
|||
Loading…
Reference in a new issue