Research-Stack/4-Infrastructure/shim/fsdu_hyperheuristic_bridge.py
2026-05-11 22:18:31 -05:00

211 lines
8 KiB
Python

#!/usr/bin/env python3
"""Bridge hyper-heuristic demo metrics into FSDU scar-differential receipts.
This is a receipt projection layer, not a solver. It reads the existing
hyper-heuristic orchestrator receipt and emits the dual-map FSDU accounting
surface:
ahead scar = observed speculative failure pressure
behind scar = conservative absorbed failure pressure
delta scar = ahead - behind
commit gate = bounded delta scar <= epsilon
The output is intended for dashboards, kanban, and follow-on replay checks.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import math
import time
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[2]
DEFAULT_INPUT = ROOT / "4-Infrastructure" / "shim" / "hyper_heuristic_orchestrator_receipt.json"
LOCAL_RECEIPT = ROOT / "4-Infrastructure" / "shim" / "fsdu_hyperheuristic_bridge_receipt.json"
STACK_RECEIPT = ROOT / "shared-data" / "data" / "stack_solidification" / "fsdu_hyperheuristic_bridge_receipt.json"
PROTOCOL = "fsdu_hyperheuristic_bridge_v0"
LEAN_ANCHOR = "2-Search-Space/FAMM/FAMM_FSDU.lean"
THEORY_ANCHOR = "2-Search-Space/FAMM/docs/FSDU_theory.md"
HEURISTIC_TO_SOLVER_BIAS = {
"adaptive": "a_star",
"balanced": "dijkstra",
"greedy": "greedy",
"conservative": "bfs",
"random": "dfs",
}
def stable_json(value: Any) -> str:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
def sha256_text(text: str) -> str:
return hashlib.sha256(text.encode("utf-8")).hexdigest()
def sha256_path(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
h.update(chunk)
return h.hexdigest()
def normalize_mixture(best_heuristic: str) -> dict[str, float]:
weights = {
"bfs": 0.18,
"dfs": 0.14,
"dijkstra": 0.20,
"a_star": 0.26,
"greedy": 0.22,
}
solver = HEURISTIC_TO_SOLVER_BIAS.get(best_heuristic, "a_star")
weights[solver] += 0.25
total = sum(weights.values())
return {key: round(value / total, 9) for key, value in weights.items()}
def component_alerts(success_rate: float, failure_pressure: float, heuristic_count: int) -> list[str]:
alerts: list[str] = []
if success_rate < 0.75:
alerts.append("heuristicBiasFailed")
if success_rate < 0.70:
alerts.append("loopPressureRising")
if failure_pressure >= 3.0:
alerts.append("deadEndConfirmed")
if heuristic_count >= 4 and success_rate >= 0.75:
alerts.append("shortcutOpened")
if not alerts:
alerts.append("edgeCostChanged")
return alerts
def project_component(component: dict[str, Any], epsilon: float) -> dict[str, Any]:
demo = component.get("demo_results", {})
success_rate = float(demo.get("success_rate", 0.0))
operations = int(demo.get("total_operations", 0))
best_heuristic = str(demo.get("best_heuristic", "adaptive"))
heuristics = component.get("heuristics", [])
heuristic_count = len(heuristics)
failure_pressure = max(0.0, 1.0 - success_rate) * operations
exploration_pressure = 0.01 * heuristic_count
ahead_scar = failure_pressure + exploration_pressure
# The behind map represents conservative absorbed error. It deliberately
# lags the ahead map; the differential is the control signal.
behind_scar = failure_pressure * success_rate
scar_delta = ahead_scar - behind_scar
abs_delta = abs(scar_delta)
admissible = abs_delta <= epsilon
return {
"component": component.get("component", "UNKNOWN"),
"description": component.get("description", ""),
"observed": {
"success_rate": success_rate,
"total_operations": operations,
"best_heuristic": best_heuristic,
"heuristic_count": heuristic_count,
},
"fsdu_projection": {
"ahead_scar": round(ahead_scar, 9),
"behind_scar": round(behind_scar, 9),
"scar_delta": round(scar_delta, 9),
"abs_scar_delta": round(abs_delta, 9),
"epsilon": epsilon,
"admissible": admissible,
"commit_decision": "COMMIT_ALLOWED" if admissible else "RETUNE_REQUIRED",
"alerts": component_alerts(success_rate, failure_pressure, heuristic_count),
"solver_mixture": normalize_mixture(best_heuristic),
},
"claim_boundary": "receipt_projection_not_live_path_optimality_not_compression_claim",
}
def build_receipt(input_path: Path, epsilon: float, out_path: Path, mirror_path: Path | None) -> dict[str, Any]:
source = json.loads(input_path.read_text(encoding="utf-8"))
components = source.get("components_implemented", [])
projected = [project_component(component, epsilon) for component in components]
all_admissible = all(row["fsdu_projection"]["admissible"] for row in projected)
max_delta = max((row["fsdu_projection"]["abs_scar_delta"] for row in projected), default=0.0)
receipt = {
"protocol": PROTOCOL,
"created_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"source_receipt": {
"path": str(input_path),
"sha256": sha256_path(input_path),
"script_name": source.get("script_name"),
"source_timestamp": source.get("timestamp"),
},
"lean_anchor": LEAN_ANCHOR,
"theory_anchor": THEORY_ANCHOR,
"equation": {
"state": "X_t = (M_a, M_b, S_a, S_b, Theta)",
"scar_differential": "DeltaS_t = S_a - S_b",
"commit_gate": "commit allowed iff ||DeltaS_t|| <= epsilon",
},
"projection_policy": {
"epsilon": epsilon,
"ahead_scar": "(1 - success_rate) * total_operations + 0.01 * heuristic_count",
"behind_scar": "(1 - success_rate) * total_operations * success_rate",
"live_path_claim": False,
"compression_claim": False,
"hardware_claim": False,
},
"components": projected,
"gate": {
"decision": "ADMIT_FSDU_BRIDGE_RECEIPT" if all_admissible else "HOLD_SCAR_DIVERGENCE",
"all_components_admissible": all_admissible,
"component_count": len(projected),
"max_abs_scar_delta": round(max_delta, 9),
"epsilon": epsilon,
"next_gate": "wire live orchestrator state snapshots into the same FSDU fields",
},
}
receipt["receipt_hash"] = sha256_text(stable_json(receipt))
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n", encoding="utf-8")
if mirror_path is not None:
mirror_path.parent.mkdir(parents=True, exist_ok=True)
mirror_path.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n", encoding="utf-8")
return receipt
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--input", default=str(DEFAULT_INPUT), help="Hyper-heuristic receipt JSON")
parser.add_argument("--epsilon", type=float, default=1.25, help="Scar differential commit envelope")
parser.add_argument("--output", default=str(LOCAL_RECEIPT), help="Receipt output path")
parser.add_argument("--no-mirror", action="store_true", help="Skip shared-data mirror receipt")
args = parser.parse_args()
mirror = None if args.no_mirror else STACK_RECEIPT
receipt = build_receipt(Path(args.input), args.epsilon, Path(args.output), mirror)
print(
json.dumps(
{
"receipt": str(Path(args.output)),
"mirror": None if mirror is None else str(mirror),
"decision": receipt["gate"]["decision"],
"component_count": receipt["gate"]["component_count"],
"max_abs_scar_delta": receipt["gate"]["max_abs_scar_delta"],
"epsilon": receipt["gate"]["epsilon"],
},
indent=2,
sort_keys=True,
)
)
if __name__ == "__main__":
main()