62 lines
2.5 KiB
Python
62 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
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
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class SalienceMap:
|
|
indices: np.ndarray
|
|
scores: np.ndarray
|
|
budget: int
|
|
|
|
def __post_init__(self) -> None:
|
|
object.__setattr__(self, "indices", np.asarray(self.indices, dtype=np.int64).copy())
|
|
object.__setattr__(self, "scores", np.asarray(self.scores, dtype=np.float32).copy())
|
|
object.__setattr__(self, "budget", int(self.budget))
|
|
|
|
|
|
class SalienceOperator:
|
|
"""
|
|
Compute generation-facing salience from ADR-0008 field curvature.
|
|
|
|
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:
|
|
if top_k <= 0:
|
|
return SalienceMap(indices=np.asarray([], dtype=np.int64), scores=np.asarray([], dtype=np.float32), budget=0)
|
|
if len(vocab) == 0:
|
|
return SalienceMap(indices=np.asarray([], dtype=np.int64), scores=np.asarray([], dtype=np.float32), budget=0)
|
|
|
|
active = vocab.get_versor_at(field.node)
|
|
regions: list[FieldRegion] = []
|
|
for idx in range(len(vocab)):
|
|
v = vocab.get_versor_at(idx)
|
|
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,
|
|
)
|
|
)
|
|
|
|
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)
|