Tighten hot-path backend consistency
- route SessionContext anchor CGA through algebra.backend - move aspect-weight carry into FieldEnergyOperator.compute - remove duplicated propagate_step threshold patch and per-step imports - add carry_aspect_weight tests for parity, fallback, and propagation preservation - preserve normalization, propagation, vault, Rust dispatch, and energy cadence semantics
This commit is contained in:
parent
f973e61bc2
commit
15ed2cee89
4 changed files with 48 additions and 33 deletions
|
|
@ -87,12 +87,13 @@ class FieldEnergyOperator:
|
||||||
coherence_residual: float = 0.0,
|
coherence_residual: float = 0.0,
|
||||||
morphology_features: Mapping[str, object] | None = None,
|
morphology_features: Mapping[str, object] | None = None,
|
||||||
anchor_adjacent: bool = False,
|
anchor_adjacent: bool = False,
|
||||||
|
carry_aspect_weight: float = 0.0,
|
||||||
) -> EnergyProfile:
|
) -> EnergyProfile:
|
||||||
convergence = min(log1p(max(0, convergence_density)) / log1p(8), 1.0)
|
convergence = min(log1p(max(0, convergence_density)) / log1p(8), 1.0)
|
||||||
age = max(0, int(current_cycle) - int(last_activation_cycle))
|
age = max(0, int(current_cycle) - int(last_activation_cycle))
|
||||||
recency = min(max(0, activation_count), 8) / 8.0 * exp(-age / 12.0)
|
recency = min(max(0, activation_count), 8) / 8.0 * exp(-age / 12.0)
|
||||||
residual = min(max(0.0, float(coherence_residual)), 1.0)
|
residual = min(max(0.0, float(coherence_residual)), 1.0)
|
||||||
aspect = aspect_weight(morphology_features)
|
aspect = carry_aspect_weight if carry_aspect_weight > 0.0 else aspect_weight(morphology_features)
|
||||||
raw = (0.35 * convergence) + (0.25 * recency) + (0.20 * residual) + (0.20 * aspect)
|
raw = (0.35 * convergence) + (0.25 * recency) + (0.20 * residual) + (0.20 * aspect)
|
||||||
if anchor_adjacent and raw >= 0.72:
|
if anchor_adjacent and raw >= 0.72:
|
||||||
energy_class = EnergyClass.E4
|
energy_class = EnergyClass.E4
|
||||||
|
|
|
||||||
|
|
@ -55,38 +55,10 @@ def propagate_step(state: FieldState, V) -> FieldState:
|
||||||
current_cycle=new_step,
|
current_cycle=new_step,
|
||||||
last_activation_cycle=ep.last_activation_cycle,
|
last_activation_cycle=ep.last_activation_cycle,
|
||||||
coherence_residual=0.0,
|
coherence_residual=0.0,
|
||||||
morphology_features=None, # aspect weight baked at injection; not re-read here
|
morphology_features=None,
|
||||||
anchor_adjacent=ep.anchor_adjacent,
|
anchor_adjacent=ep.anchor_adjacent,
|
||||||
|
carry_aspect_weight=ep.aspect_weight,
|
||||||
)
|
)
|
||||||
# Carry the baked aspect_weight forward: the operator won't re-derive
|
|
||||||
# it from morphology_features=None, so we patch the raw score to
|
|
||||||
# preserve the aspect contribution that was set at the gate.
|
|
||||||
if ep.aspect_weight > 0.0:
|
|
||||||
from dataclasses import replace as _replace
|
|
||||||
# Recompute with the original aspect weight patched back in:
|
|
||||||
# raw already accounts for convergence/recency/residual from above.
|
|
||||||
# We rebuild raw adding the aspect component the operator lost.
|
|
||||||
patched_raw = new_energy.raw + 0.20 * ep.aspect_weight
|
|
||||||
patched_raw = min(patched_raw, 1.0)
|
|
||||||
from core.physics.energy import EnergyClass as _EC
|
|
||||||
if ep.anchor_adjacent and patched_raw >= 0.72:
|
|
||||||
patched_class = _EC.E4
|
|
||||||
elif patched_raw >= 0.82:
|
|
||||||
patched_class = _EC.E4
|
|
||||||
elif patched_raw >= 0.62:
|
|
||||||
patched_class = _EC.E3
|
|
||||||
elif patched_raw >= 0.38:
|
|
||||||
patched_class = _EC.E2
|
|
||||||
elif patched_raw >= 0.16:
|
|
||||||
patched_class = _EC.E1
|
|
||||||
else:
|
|
||||||
patched_class = _EC.E0
|
|
||||||
new_energy = _replace(
|
|
||||||
new_energy,
|
|
||||||
raw=patched_raw,
|
|
||||||
energy_class=patched_class,
|
|
||||||
aspect_weight=ep.aspect_weight,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
new_energy = None
|
new_energy = None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from algebra.backend import versor_apply
|
from algebra.backend import cga_inner, versor_apply
|
||||||
from algebra.cga import cga_inner, outer_product
|
from algebra.cga import outer_product
|
||||||
from field.state import FieldState
|
from field.state import FieldState
|
||||||
from generate.dialogue import DialogueTurn
|
from generate.dialogue import DialogueTurn
|
||||||
from generate.proposition import Proposition
|
from generate.proposition import Proposition
|
||||||
|
|
|
||||||
|
|
@ -344,6 +344,48 @@ class TestPropagateStepEnergyRecomputation:
|
||||||
assert state.energy.energy_class is not EnergyClass.E4
|
assert state.energy.energy_class is not EnergyClass.E4
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# carry_aspect_weight consolidation
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestCarryAspectWeight:
|
||||||
|
def test_carry_aspect_matches_morphology_derived(self):
|
||||||
|
"""carry_aspect_weight produces identical raw/class as morphology-derived aspect."""
|
||||||
|
base_kw = dict(
|
||||||
|
convergence_density=4,
|
||||||
|
activation_count=4,
|
||||||
|
current_cycle=1,
|
||||||
|
last_activation_cycle=0,
|
||||||
|
coherence_residual=0.0,
|
||||||
|
anchor_adjacent=False,
|
||||||
|
)
|
||||||
|
from_morph = _op.compute(**base_kw, morphology_features={"mood": "imperative"})
|
||||||
|
from_carry = _op.compute(**base_kw, carry_aspect_weight=0.90)
|
||||||
|
assert from_carry.raw == pytest.approx(from_morph.raw)
|
||||||
|
assert from_carry.energy_class is from_morph.energy_class
|
||||||
|
assert from_carry.aspect_weight == pytest.approx(from_morph.aspect_weight)
|
||||||
|
|
||||||
|
def test_carry_zero_falls_back_to_morphology(self):
|
||||||
|
ep = _op.compute(
|
||||||
|
morphology_features={"aspect": "yiqtol"},
|
||||||
|
carry_aspect_weight=0.0,
|
||||||
|
)
|
||||||
|
assert ep.aspect_weight == pytest.approx(0.65)
|
||||||
|
|
||||||
|
def test_propagate_step_preserves_baked_aspect_weight(self):
|
||||||
|
"""Aspect weight injected at the gate survives propagation via carry."""
|
||||||
|
ep = _op.compute(
|
||||||
|
convergence_density=4,
|
||||||
|
activation_count=2,
|
||||||
|
current_cycle=0,
|
||||||
|
morphology_features={"mood": "imperative"},
|
||||||
|
)
|
||||||
|
state = FieldState(F=_clean_versor(), node=0, step=0, energy=ep)
|
||||||
|
new_state = propagate_step(state, _identity_rotor())
|
||||||
|
assert new_state.energy.aspect_weight == pytest.approx(0.90)
|
||||||
|
assert new_state.energy.raw > 0.0
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# EnergyProfile field storage round-trip on FieldState
|
# EnergyProfile field storage round-trip on FieldState
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue