Artifex/gpu_feature_parity_contract_v1_2.py

244 lines
16 KiB
Python
Raw Permalink Normal View History

"""GPU_FEATURE_PARITY_CONTRACT_V1_2 state-first validation contract."""
from __future__ import annotations
import hashlib
import json
from typing import Any, Mapping
import numpy as np
ARTIFACT = "GPU_FEATURE_PARITY_CONTRACT_V1_2"
CALIBRATION_ARTIFACT = "GPU_FEATURE_PARITY_CALIBRATION_V1_2"
VALIDATION_ARTIFACT = "GPU_FEATURE_PARITY_VALIDATION_V1_2"
ROLE_SURFACE_ARTIFACT = "GPU_FEATURE_PARITY_ROLE_SURFACE_V1_2"
REVIEW_TEMPLATE_ARTIFACT = "GPU_FEATURE_PARITY_REVIEW_TEMPLATE_V1_2"
ROLE_A_EXACT = "A_EXACT"
ROLE_B_BOUNDED = "B_BOUNDED"
ROLE_C_UNVERIFIABLE = "C_UNVERIFIABLE"
UNVERIFIABLE = "UNVERIFIABLE_FROM_RECOVERED_SOURCE"
SUPERTREND_TRACE_KEYS = frozenset(("output", "true_range", "atr", "basic_upper", "basic_lower", "upper", "lower", "direction", "initialized", "prior_close_le_upper", "prior_close_ge_lower", "active_long", "close_ge_lower", "close_le_upper", "output_uses_lower", "direction_transition"))
EXACT_TRACE_KEYS = SUPERTREND_TRACE_KEYS - {"true_range", "atr", "basic_upper", "basic_lower"}
PREDICATE_KEYS = EXACT_TRACE_KEYS - {"output", "upper", "lower", "direction", "initialized", "direction_transition"}
class FrozenContractError(ValueError):
pass
def canonical_bytes(value: object) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode()
def _digest(value: object) -> str:
return hashlib.sha256(canonical_bytes(value)).hexdigest()
def _same(expected: np.ndarray, actual: np.ndarray) -> bool:
return expected.shape == actual.shape and np.array_equal(expected, actual, equal_nan=True)
def compare_supertrend_trace(expected: Mapping[str, np.ndarray], actual: Mapping[str, np.ndarray]) -> dict[str, Any]:
"""Check all discrete states and branch predicates exactly; ATR is excluded."""
required = EXACT_TRACE_KEYS | {"atr"}
missing = sorted(required - set(expected) | required - set(actual))
mismatched = sorted(key for key in EXACT_TRACE_KEYS if key in expected and key in actual and not _same(np.asarray(expected[key]), np.asarray(actual[key])))
atr = exact_output(np.asarray(expected["atr"]), np.asarray(actual["atr"])) if "atr" in expected and "atr" in actual else None
atr_structure_exact = atr is not None and atr["shape_exact"] and atr["nan_mask_exact"]
state_exact = not missing and not mismatched
return {"state_exact": state_exact, "required_keys": sorted(required), "mismatched_exact_keys": mismatched, "missing_keys": missing, "atr": atr, "atr_structure_exact": atr_structure_exact, "branch_predicates_exact": state_exact and all(key not in mismatched for key in PREDICATE_KEYS), "transition_checks_exact": state_exact}
def atr_metrics(expected: np.ndarray, actual: np.ndarray) -> dict[str, float]:
finite = np.isfinite(expected) & np.isfinite(actual)
delta = np.abs(np.asarray(actual)[finite] - np.asarray(expected)[finite])
return {"max_absolute_error": float(delta.max()) if delta.size else 0.0, "mae": float(delta.mean()) if delta.size else 0.0}
def output_metrics(expected: np.ndarray, actual: np.ndarray) -> dict[str, float]:
"""Stable, JSON-safe continuous-output measurements."""
finite = np.isfinite(expected) & np.isfinite(actual)
delta = np.abs(np.asarray(actual)[finite] - np.asarray(expected)[finite])
denominator = np.abs(np.asarray(expected)[finite])
relative = delta[denominator != 0] / denominator[denominator != 0]
return {
"max_absolute_error": float(delta.max()) if delta.size else 0.0,
"max_relative_error": float(relative.max()) if relative.size else 0.0,
"mae": float(delta.mean()) if delta.size else 0.0,
}
def exact_output(expected: np.ndarray, actual: np.ndarray) -> dict[str, Any]:
return {
"shape_exact": expected.shape == actual.shape,
"dtype_exact": expected.dtype == actual.dtype,
"nan_mask_exact": expected.shape == actual.shape
and np.array_equal(np.isnan(expected), np.isnan(actual)),
"values_exact": _same(expected, actual),
}
def family_for_indicator(indicator_id: int) -> str:
return {17: "bollinger", 18: "bollinger", 19: "supertrend", 20: "donchian", 21: "donchian", 23: "keltner", 24: "keltner", 28: "psar"}[indicator_id]
def cpu_supertrend_trace(close: np.ndarray, high: np.ndarray, low: np.ndarray, period: int, multiplier: float) -> dict[str, np.ndarray]:
"""Historical Supertrend semantics with explicit V1.2 branch evidence."""
from control_plane.trading_studio.indicators.historical_band_channel import _rma, _true_range
n = len(close)
true_range = _true_range(high, low, close)
atr = _rma(true_range, period)
output = np.full(n, np.nan)
upper, lower, basic_upper, basic_lower = output.copy(), output.copy(), output.copy(), output.copy()
direction = np.zeros(n, dtype=np.int8)
initialized = np.zeros(n, dtype=bool)
predicates = {name: np.zeros(n, dtype=bool) for name in PREDICATE_KEYS}
direction_transition = np.zeros(n, dtype=bool)
for index in range(period, n):
midpoint = (high[index] + low[index]) / 2.0
basic_upper[index], basic_lower[index] = midpoint + multiplier * atr[index], midpoint - multiplier * atr[index]
if index == period:
upper[index], lower[index] = basic_upper[index], basic_lower[index]
predicates["close_ge_lower"][index] = close[index] > lower[index]
predicates["output_uses_lower"][index] = predicates["close_ge_lower"][index]
output[index] = lower[index] if predicates["output_uses_lower"][index] else upper[index]
else:
predicates["prior_close_le_upper"][index] = close[index - 1] <= upper[index - 1]
predicates["prior_close_ge_lower"][index] = close[index - 1] >= lower[index - 1]
upper[index] = min(basic_upper[index], upper[index - 1]) if predicates["prior_close_le_upper"][index] else basic_upper[index]
lower[index] = max(basic_lower[index], lower[index - 1]) if predicates["prior_close_ge_lower"][index] else basic_lower[index]
predicates["active_long"][index] = direction[index - 1] == 1
predicates["close_ge_lower"][index] = close[index] >= lower[index]
predicates["close_le_upper"][index] = close[index] <= upper[index]
predicates["output_uses_lower"][index] = predicates["active_long"][index] and predicates["close_ge_lower"][index] or not predicates["active_long"][index] and not predicates["close_le_upper"][index]
output[index] = lower[index] if predicates["output_uses_lower"][index] else upper[index]
direction[index] = 1 if close[index] > output[index] else -1
initialized[index] = True
if index > period:
direction_transition[index] = direction[index] != direction[index - 1]
return {
"output": output,
"true_range": true_range,
"atr": atr,
"basic_upper": basic_upper,
"basic_lower": basic_lower,
"upper": upper,
"lower": lower,
"direction": direction,
"initialized": initialized,
"direction_transition": direction_transition,
**predicates,
}
def calibrate_supertrend(request_id: str, expected: Mapping[str, np.ndarray], actual: Mapping[str, np.ndarray], *, atr_limits: Mapping[str, float] | None = None) -> dict[str, Any]:
trace = compare_supertrend_trace(expected, actual)
measured = atr_metrics(np.asarray(expected["atr"]), np.asarray(actual["atr"])) if trace["state_exact"] and trace["atr_structure_exact"] else None
return {"request_id": request_id, "family": "supertrend", "role": ROLE_A_EXACT, "role_classification": {"discrete_state_and_transitions": ROLE_A_EXACT, "atr": ROLE_B_BOUNDED}, "trace": trace, "atr": measured, "atr_limits": dict(atr_limits) if atr_limits is not None else measured}
def calibrate_output(request_id: str, indicator_id: int, expected: np.ndarray, actual: np.ndarray) -> dict[str, Any]:
"""Calibrate a non-Supertrend Batch01 output under its family evidence class."""
family = family_for_indicator(indicator_id)
exact = family in {"donchian", "psar"}
result = exact_output(expected, actual)
return {
"request_id": request_id,
"indicator_id": indicator_id,
"family": family,
"role": ROLE_A_EXACT if exact else ROLE_B_BOUNDED,
"output": result,
# Bounds are deliberately emitted only for continuous families.
"limits": None if exact else output_metrics(expected, actual),
}
def freeze_contract(calibration: Mapping[str, Any]) -> dict[str, Any]:
if calibration.get("artifact") != CALIBRATION_ARTIFACT:
raise FrozenContractError("only V1.2 calibration evidence can be frozen")
limits = {}
for record in calibration.get("records", []):
if record.get("family") == "supertrend":
if not record["trace"]["state_exact"] or not record["trace"]["atr_structure_exact"] or record["atr"] is None:
raise FrozenContractError("cannot freeze a state-inexact calibration")
limits[record["request_id"]] = {"family": "supertrend", "role": ROLE_A_EXACT, "atr_limits": dict(record["atr_limits"])}
elif record.get("family") in {"donchian", "psar"}:
if not all(record["output"].values()):
raise FrozenContractError("cannot freeze an inexact discrete/state calibration")
limits[record["request_id"]] = {"family": record["family"], "role": ROLE_A_EXACT}
elif record.get("family") in {"bollinger", "keltner"}:
if not record["output"]["shape_exact"] or not record["output"]["nan_mask_exact"]:
raise FrozenContractError("cannot freeze a structurally inexact continuous calibration")
limits[record["request_id"]] = {"family": record["family"], "role": ROLE_B_BOUNDED, "output_limits": dict(record["limits"])}
else:
raise FrozenContractError("unknown Batch01 family in calibration")
return {"artifact": ARTIFACT, "schema_version": "1.2", "status": "frozen", "role_classification": {"discrete_state_and_transitions": ROLE_A_EXACT, "atr": ROLE_B_BOUNDED, "unrecovered_batch01_roles": ROLE_C_UNVERIFIABLE}, "calibration_sha256": _digest(calibration), "feature_limits": limits}
def validate_all_frozen_contract(contract: Mapping[str, Any], expected_outputs: Mapping[str, np.ndarray], actual_outputs: Mapping[str, np.ndarray], traces: Mapping[str, tuple[Mapping[str, np.ndarray], Mapping[str, np.ndarray]]]) -> dict[str, Any]:
"""Validate every frozen Batch01 feature without deriving new limits."""
if contract.get("artifact") != ARTIFACT or contract.get("status") != "frozen":
raise FrozenContractError("provided contract is not frozen GPU_FEATURE_PARITY_CONTRACT_V1_2")
limits = contract.get("feature_limits", {})
if set(limits) != set(expected_outputs) or set(limits) != set(actual_outputs):
raise FrozenContractError("contract and outputs must name exactly the same features")
records = []
for request_id, limit in limits.items():
family = limit["family"]
expected, actual = np.asarray(expected_outputs[request_id]), np.asarray(actual_outputs[request_id])
if family == "supertrend":
if request_id not in traces:
raise FrozenContractError("Supertrend trace missing")
trace = compare_supertrend_trace(*traces[request_id])
atr = atr_metrics(np.asarray(traces[request_id][0]["atr"]), np.asarray(traces[request_id][1]["atr"])) if trace["state_exact"] and trace["atr_structure_exact"] else None
bounded = atr is not None and all(atr[key] <= float(limit["atr_limits"][key]) for key in atr)
passed = trace["state_exact"] and trace["atr_structure_exact"] and bounded
records.append({"request_id": request_id, "family": family, "role": ROLE_A_EXACT, "trace": trace, "atr": atr, "atr_bounded": bounded, "passed": passed})
elif family in {"donchian", "psar"}:
output = exact_output(expected, actual)
records.append({"request_id": request_id, "family": family, "role": ROLE_A_EXACT, "output": output, "passed": all(output.values())})
else:
output = exact_output(expected, actual)
metrics = output_metrics(expected, actual)
bounded = all(metrics[key] <= float(limit["output_limits"][key]) for key in metrics)
passed = output["shape_exact"] and output["nan_mask_exact"] and bounded
records.append({"request_id": request_id, "family": family, "role": ROLE_B_BOUNDED, "output": output, "metrics": metrics, "bounded": bounded, "passed": passed})
return {"artifact": VALIDATION_ARTIFACT, "schema_version": "1.2", "contract_sha256": _digest(contract), "passed": all(record["passed"] for record in records), "records": records}
def validate_frozen_contract(contract: Mapping[str, Any], traces: Mapping[str, tuple[Mapping[str, np.ndarray], Mapping[str, np.ndarray]]]) -> dict[str, Any]:
"""Mechanical validation only: it never derives or expands frozen limits."""
if contract.get("artifact") != ARTIFACT or contract.get("schema_version") != "1.2" or contract.get("status") != "frozen":
raise FrozenContractError("provided contract is not frozen GPU_FEATURE_PARITY_CONTRACT_V1_2")
limits = contract.get("feature_limits")
if not isinstance(limits, dict) or set(limits) != set(traces):
raise FrozenContractError("contract and traces must name exactly the same features")
records = []
for request_id, (expected, actual) in traces.items():
trace = compare_supertrend_trace(expected, actual)
metric = atr_metrics(np.asarray(expected["atr"]), np.asarray(actual["atr"])) if trace["state_exact"] and trace["atr_structure_exact"] else None
bounded = metric is not None and all(metric[name] <= float(limits[request_id]["atr_limits"][name]) for name in metric)
records.append({"request_id": request_id, "role": ROLE_A_EXACT, "role_classification": {"discrete_state_and_transitions": ROLE_A_EXACT, "atr": ROLE_B_BOUNDED}, "trace": trace, "atr": metric, "atr_bounded": bounded, "passed": trace["state_exact"] and trace["atr_structure_exact"] and bounded})
return {"artifact": VALIDATION_ARTIFACT, "schema_version": "1.2", "contract_sha256": _digest(contract), "passed": all(record["passed"] for record in records), "records": records}
def role_surface(requests: list[Mapping[str, Any]]) -> dict[str, Any]:
"""Classify only recovered Cohort001 role assignments; unknown Batch01 roles do not block parity."""
try:
from control_plane.trading_studio.indicators.registry import _HS22_REQUIRED_TRIPLES
except ModuleNotFoundError:
recovered = {}
unavailable_reason = "control_plane.trading_studio.indicators.registry is unavailable"
else:
recovered = {(indicator, period, p1): slot for indicator, period, p1, slot in _HS22_REQUIRED_TRIPLES}
unavailable_reason = None
records = []
for item in requests:
key = (int(item["indicator_id"]), int(item["period"]), float(item["p1"]))
slot = recovered.get(key)
records.append({"request_id": str(item["request_id"]), "indicator_id": key[0], "period": key[1], "p1": key[2], "cohort001_role": slot, "classification": ROLE_A_EXACT if key[0] == 19 and slot else ROLE_C_UNVERIFIABLE, "role_status": "RECOVERED_COHORT001" if slot else UNVERIFIABLE, "reason": unavailable_reason if not slot else None, "parity_blocker": False if not slot else True})
return {"artifact": ROLE_SURFACE_ARTIFACT, "schema_version": "1.2", "source": "Cohort001 recovered role surface", "records": records}
def review_template() -> dict[str, Any]:
return {"artifact": REVIEW_TEMPLATE_ARTIFACT, "schema_version": "1.2", "status": "review_required_not_validated", "required_review": ["calibration evidence is separate from holdout and adversarial evidence", "exact state, transition, and branch-predicate checks pass", "ATR bounded checks were evaluated only after exact state validation", "C-unverifiable roles are recorded and excluded as parity blockers"], "decision": None}