mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-06 20:43:46 +08:00
572 lines
27 KiB
Python
572 lines
27 KiB
Python
"""PRIVATE source-only candidate. Pure arithmetic over caller-verified evidence.
|
|
|
|
This module DOES NOT verify CAN/native signatures, controller replay, profile
|
|
admission or persisted authority. The real child must recompute those before
|
|
constructing VerifiedCommandTape and again bind the result to its activation
|
|
and current-admission proofs. A qualified result is only an affine plant test.
|
|
Clock-budget helper only; no file I/O, controllers, executable callbacks or optimization
|
|
against heldout data. Protocol constants cannot be passed as tuning arguments.
|
|
"""
|
|
from bisect import bisect_right
|
|
from openpilot.starpilot.flm.producer_clock_equivalence import clock_uncertainty_limit
|
|
from dataclasses import asdict, dataclass
|
|
import hashlib
|
|
import json
|
|
import math
|
|
|
|
PROTOCOL = 'source-pose-plant-lag-v4-private'
|
|
PHYSICAL_PROTOCOL = 'source-pose-physical-observation-lag-v1-private'
|
|
LAGS_NS = tuple(range(0, 500_000_001, 10_000_000))
|
|
JOIN_NS = 30_000_000
|
|
MIN_EXCITATION = .05
|
|
MAX_RMSE = .05
|
|
MAX_COMMANDS = 12_000
|
|
MAX_POSES = 8 * 240
|
|
TRAIN_WINDOWS = 4
|
|
NUM_WINDOWS = 8
|
|
STRICT_SUPPORT = 'strict-heldout-domain-v1'
|
|
TRAINING_DOMAIN_ONLY = 'training-domain-only-heldout-v1'
|
|
LAG_STABILITY_NS = 20_000_000
|
|
TIE_ABS_MSE = 1e-12
|
|
|
|
|
|
def digest(value):
|
|
return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':'), allow_nan=False).encode()).hexdigest()
|
|
|
|
|
|
def _hash(value):
|
|
return type(value) is str and len(value) == 64 and all(c in '0123456789abcdef' for c in value)
|
|
|
|
|
|
def _integer(value, *, zero=False):
|
|
return type(value) is int and (value >= 0 if zero else value > 0)
|
|
|
|
|
|
def _number(value):
|
|
return type(value) in (int, float) and math.isfinite(value)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SourceBinding:
|
|
# All hashes are independently computed by the actual child, never read as
|
|
# authority from parent saved healthy/normalized flags.
|
|
identity: str
|
|
cp_sha256: str
|
|
source_sha256: str
|
|
controller_revision: str
|
|
source_epoch: str
|
|
baseline_revision: str
|
|
native_identity: str
|
|
calibration_generation: str
|
|
vehicle_model_generation: str
|
|
session: str
|
|
source_clock_contract_sha256: str
|
|
native_required: bool
|
|
timestamp_convention: str = 'original-kf-bounded-source-clock-v3'
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DeliveredCommand:
|
|
control_ns: int
|
|
state_ns: int
|
|
apply_ns: int
|
|
# Oriented coordinate MUST be -delivered(output) from original CAN verifier.
|
|
command: float
|
|
source_receipt_ns: int
|
|
native_observed_ns: int
|
|
native_expires_ns: int
|
|
carrier_frame: int
|
|
carrier_observed_ns: int
|
|
encoded_witness_sha256: str
|
|
control_sha256: str
|
|
applied_sha256: str
|
|
identity: str
|
|
source_epoch: str
|
|
baseline_revision: str
|
|
native_identity: str
|
|
native_required: bool = True
|
|
healthy: bool = True
|
|
neutral_coordinates: bool = True
|
|
native_allowed: bool = True
|
|
overridden: bool = False
|
|
saturated: bool = False
|
|
limited: bool = False
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class VerifiedCommandTape:
|
|
commands: tuple[DeliveredCommand, ...]
|
|
binding_digest: str
|
|
# Must equal command_tape_digest(binding, commands). This is tamper binding,
|
|
# not a cryptographic certificate that caller actually ran a verifier.
|
|
verified_tape_digest: str
|
|
verifier_source_sha256: str
|
|
verified_source_native_can: bool
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SourcePose:
|
|
pose_id: str
|
|
mono_ns: int
|
|
received_ns: int
|
|
source_timestamp_ns: int
|
|
source_clock_domain: str
|
|
clock_offset_ns: int
|
|
clock_uncertainty_ns: int
|
|
source_lower_ns: int
|
|
source_upper_ns: int
|
|
clock_generation: int
|
|
speed_mps: float
|
|
calibrated_yaw_rate: float
|
|
calibrated_roll_rad: float
|
|
binding_digest: str
|
|
healthy: bool = True
|
|
native_allowed_at_receipt: bool = True
|
|
overridden: bool = False
|
|
fault: bool = False
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CalibrationWindow:
|
|
# Caller recomputes original whole source-window boundaries independently.
|
|
block_id: str
|
|
original_start_ns: int
|
|
original_end_ns: int
|
|
pose_ids: tuple[str, ...]
|
|
source_window_sha256: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Pair:
|
|
block_id: str
|
|
pose_id: str
|
|
command_index: int
|
|
command_ns: int
|
|
response_ns: int
|
|
command: float
|
|
response: float
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class LagDecision:
|
|
protocol: str
|
|
lag_ns: int
|
|
slope: float
|
|
intercept: float
|
|
command_low: float
|
|
command_high: float
|
|
train_block_ids: tuple[str, ...]
|
|
train_pose_ids: tuple[str, ...]
|
|
train_source_digest: str
|
|
training_pairs_digest: str
|
|
training_scores: tuple[tuple[int, float | None], ...]
|
|
training_leave_one_out_lags: tuple[int, ...]
|
|
# Contains NO heldout response primitives, scores or outcome.
|
|
digest: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class IdentificationResult:
|
|
status: str
|
|
reason: str
|
|
binding_digest: str
|
|
experiment_digest: str
|
|
decision: LagDecision | None
|
|
rows: tuple[Pair, ...]
|
|
heldout_blocks: tuple[tuple[str, float, int], ...]
|
|
model_receipt_digest: str | None
|
|
cutoff_ns: int | None
|
|
support_policy: str = STRICT_SUPPORT
|
|
|
|
|
|
def binding_digest(binding):
|
|
return digest(asdict(binding))
|
|
|
|
|
|
def command_tape_digest(binding, commands):
|
|
return digest({'protocol': PROTOCOL, 'binding': asdict(binding), 'commands': [asdict(c) for c in commands]})
|
|
|
|
|
|
def _validate(binding, tape, poses, windows):
|
|
if not isinstance(binding, SourceBinding) or not isinstance(tape, VerifiedCommandTape):
|
|
raise ValueError('explicit source binding and independently verified command tape required')
|
|
fields = asdict(binding)
|
|
if (any(not _hash(value) for key, value in fields.items() if key not in ('session', 'timestamp_convention', 'native_required', 'native_identity'))
|
|
or type(binding.native_required) is not bool
|
|
or (not _hash(binding.native_identity) if binding.native_required else binding.native_identity != '')
|
|
or type(binding.session) is not str or not binding.session
|
|
or binding.timestamp_convention != 'original-kf-bounded-source-clock-v3'):
|
|
raise ValueError('source/native/CP/calibration/model binding')
|
|
bound = binding_digest(binding)
|
|
if (type(tape.commands) is not tuple or not 1 <= len(tape.commands) <= MAX_COMMANDS
|
|
or tape.binding_digest != bound or not _hash(tape.verifier_source_sha256)
|
|
or tape.verified_source_native_can is not True
|
|
or tape.verified_tape_digest != command_tape_digest(binding, tape.commands)):
|
|
raise ValueError('exact independently verified tape binding/bounds')
|
|
previous = 0
|
|
previous_carrier = 0
|
|
for c in tape.commands:
|
|
if not isinstance(c, DeliveredCommand):
|
|
raise ValueError('actual delivered command type')
|
|
integers = (c.control_ns, c.state_ns, c.apply_ns, c.source_receipt_ns,
|
|
c.carrier_frame, c.carrier_observed_ns)
|
|
if (not all(_integer(v) for v in integers) or c.apply_ns <= previous or
|
|
not 0 < c.state_ns <= c.control_ns <= c.state_ns + 30_000_000 or
|
|
not c.control_ns <= c.apply_ns <= c.control_ns + 100_000_000 or
|
|
not 0 < c.source_receipt_ns <= c.control_ns <= c.source_receipt_ns + 500_000_000 or
|
|
c.native_required is not binding.native_required or
|
|
(not all(type(v) is int for v in (c.native_observed_ns,c.native_expires_ns))) or
|
|
(not 0 < c.native_observed_ns <= c.control_ns <= c.apply_ns <= c.native_expires_ns or
|
|
c.native_expires_ns-c.native_observed_ns > 200_000_000 if c.native_required else
|
|
(c.native_observed_ns,c.native_expires_ns) != (0,0)) or
|
|
not c.apply_ns <= c.carrier_observed_ns <= c.apply_ns + 100_000_000 or
|
|
previous_carrier and c.apply_ns - previous <= JOIN_NS and c.carrier_frame not in (previous_carrier, previous_carrier + 1) or
|
|
not _number(c.command) or abs(c.command) > 1 or
|
|
any(not _hash(v) for v in (c.encoded_witness_sha256, c.control_sha256, c.applied_sha256)) or
|
|
(c.identity, c.source_epoch, c.baseline_revision, c.native_identity) !=
|
|
(binding.identity, binding.source_epoch, binding.baseline_revision, binding.native_identity) or
|
|
any(v is not True for v in (c.healthy, c.neutral_coordinates, c.native_allowed)) or
|
|
any(v is not False for v in (c.overridden, c.saturated, c.limited))):
|
|
raise ValueError('actual source/native/receipt/clock/neutral/unclipped command guard')
|
|
previous = c.apply_ns
|
|
previous_carrier = c.carrier_frame
|
|
if type(poses) is not tuple or not 1 <= len(poses) <= MAX_POSES or type(windows) is not tuple or len(windows) != NUM_WINDOWS:
|
|
raise ValueError('exact eight calibration windows / bounded original poses')
|
|
previous = 0
|
|
pose_ids = set()
|
|
for p in poses:
|
|
if not isinstance(p, SourcePose):
|
|
raise ValueError('unpaired independent source pose type')
|
|
if (not _hash(p.pose_id) or p.pose_id in pose_ids or
|
|
not all(_integer(v) for v in (p.mono_ns, p.received_ns, p.source_timestamp_ns)) or
|
|
type(p.clock_offset_ns) is not int or not _integer(p.clock_uncertainty_ns, zero=True) or
|
|
p.clock_uncertainty_ns > clock_uncertainty_limit(p.source_clock_domain) or p.source_timestamp_ns - p.clock_offset_ns != p.mono_ns or
|
|
p.source_clock_domain not in ('monotonic', 'boottime', 'unknown-equivalent-at-receipt', 'mono_boot_equivalent_interval_v1') or
|
|
p.source_clock_domain != 'boottime' and p.clock_offset_ns != 0 or
|
|
not all(_integer(v) for v in (p.source_lower_ns,p.source_upper_ns)) or
|
|
not _integer(p.clock_generation,zero=True) or
|
|
not p.source_lower_ns <= p.mono_ns <= p.source_upper_ns or
|
|
max(p.mono_ns-p.source_lower_ns,p.source_upper_ns-p.mono_ns) > p.clock_uncertainty_ns or
|
|
not p.mono_ns <= p.received_ns <= p.mono_ns + 100_000_000 or p.mono_ns <= previous or
|
|
not all(_number(v) for v in (p.speed_mps, p.calibrated_yaw_rate, p.calibrated_roll_rad)) or
|
|
p.speed_mps <= 0 or p.binding_digest != bound or
|
|
p.clock_generation != poses[0].clock_generation or
|
|
any(v is not True for v in (p.healthy, p.native_allowed_at_receipt)) or
|
|
any(v is not False for v in (p.overridden, p.fault))):
|
|
raise ValueError('immutable source pose/clock/native/calibration guard')
|
|
previous = p.mono_ns
|
|
pose_ids.add(p.pose_id)
|
|
if poses[-1].mono_ns - poses[0].mono_ns + LAGS_NS[-1] > 600_000_000_000:
|
|
raise ValueError('original maximum 600 second physical history including candidate lookback')
|
|
mapped = {p.pose_id: p for p in poses}
|
|
assigned = []
|
|
block_ids = set()
|
|
previous = 0
|
|
for w in windows:
|
|
if (not isinstance(w, CalibrationWindow) or not _hash(w.block_id) or w.block_id in block_ids or
|
|
not _hash(w.source_window_sha256) or not _integer(w.original_start_ns) or not _integer(w.original_end_ns) or
|
|
not previous < w.original_start_ns <= w.original_end_ns or type(w.pose_ids) is not tuple or
|
|
not 40 <= len(w.pose_ids) <= 240 or any(pid not in mapped for pid in w.pose_ids)):
|
|
raise ValueError('original fixed chronological whole-window declaration')
|
|
selected = [mapped[pid] for pid in w.pose_ids]
|
|
if (tuple(sorted(selected, key=lambda p: p.mono_ns)) != tuple(selected) or
|
|
any(not w.original_start_ns <= p.mono_ns <= w.original_end_ns for p in selected)):
|
|
raise ValueError('pose outside original declared source window/order')
|
|
assigned.extend(w.pose_ids)
|
|
block_ids.add(w.block_id)
|
|
previous = w.original_end_ns
|
|
if assigned != [p.pose_id for p in poses] or len(set(assigned)) != len(assigned):
|
|
raise ValueError('all original poses must be assigned exactly once without dropping/reordering')
|
|
return bound
|
|
|
|
|
|
def _validate_physical(binding, tape, poses, windows):
|
|
from openpilot.starpilot.flm.physical_input_observation import (
|
|
PROTOCOL as OBSERVATION_PROTOCOL, VerifiedPhysicalTape, physical_tape_digest, validate_input,
|
|
)
|
|
if type(binding) is not SourceBinding or type(tape) is not VerifiedPhysicalTape:
|
|
raise ValueError('explicit physical-only binding and tape required')
|
|
fields = asdict(binding)
|
|
if (any(not _hash(value) for key, value in fields.items() if key not in ('session', 'timestamp_convention', 'native_required', 'native_identity'))
|
|
or type(binding.native_required) is not bool
|
|
or (not _hash(binding.native_identity) if binding.native_required else binding.native_identity != '')
|
|
or type(binding.session) is not str or not binding.session
|
|
or binding.timestamp_convention != 'original-kf-bounded-source-clock-v3'):
|
|
raise ValueError('source/native/CP/calibration/model binding')
|
|
bound = binding_digest(binding)
|
|
if (tape.protocol != OBSERVATION_PROTOCOL or type(tape.commands) is not tuple or
|
|
not 1 <= len(tape.commands) <= MAX_COMMANDS or tape.binding_digest != bound or
|
|
not _hash(tape.verifier_source_sha256) or
|
|
tape.verified_tape_digest != physical_tape_digest(binding, tape.commands)):
|
|
raise ValueError('physical-only complete tape custody/bounds')
|
|
previous, previous_carrier = 0, 0
|
|
for command in tape.commands:
|
|
validate_input(command, binding, bound)
|
|
if (command.generation != tape.commands[0].generation or command.apply_ns <= previous or
|
|
previous_carrier and command.apply_ns - previous <= JOIN_NS and
|
|
command.carrier_frame not in (previous_carrier, previous_carrier + 1)):
|
|
raise ValueError('physical original FIFO/carrier chronology')
|
|
previous, previous_carrier = command.apply_ns, command.carrier_frame
|
|
if type(poses) is not tuple or not 1 <= len(poses) <= MAX_POSES or type(windows) is not tuple or len(windows) != NUM_WINDOWS:
|
|
raise ValueError('exact eight calibration windows / bounded original poses')
|
|
previous = 0
|
|
pose_ids = set()
|
|
for p in poses:
|
|
if not isinstance(p, SourcePose):
|
|
raise ValueError('unpaired independent source pose type')
|
|
if (not _hash(p.pose_id) or p.pose_id in pose_ids or
|
|
not all(_integer(v) for v in (p.mono_ns, p.received_ns, p.source_timestamp_ns)) or
|
|
type(p.clock_offset_ns) is not int or not _integer(p.clock_uncertainty_ns, zero=True) or
|
|
p.clock_uncertainty_ns > clock_uncertainty_limit(p.source_clock_domain) or p.source_timestamp_ns - p.clock_offset_ns != p.mono_ns or
|
|
p.source_clock_domain not in ('monotonic', 'boottime', 'unknown-equivalent-at-receipt', 'mono_boot_equivalent_interval_v1') or
|
|
p.source_clock_domain != 'boottime' and p.clock_offset_ns != 0 or
|
|
not all(_integer(v) for v in (p.source_lower_ns,p.source_upper_ns)) or
|
|
not _integer(p.clock_generation,zero=True) or
|
|
not p.source_lower_ns <= p.mono_ns <= p.source_upper_ns or
|
|
max(p.mono_ns-p.source_lower_ns,p.source_upper_ns-p.mono_ns) > p.clock_uncertainty_ns or
|
|
not p.mono_ns <= p.received_ns <= p.mono_ns + 100_000_000 or p.mono_ns <= previous or
|
|
not all(_number(v) for v in (p.speed_mps, p.calibrated_yaw_rate, p.calibrated_roll_rad)) or
|
|
p.speed_mps <= 0 or p.binding_digest != bound or
|
|
p.clock_generation != poses[0].clock_generation or
|
|
any(v is not True for v in (p.healthy, p.native_allowed_at_receipt)) or
|
|
any(v is not False for v in (p.overridden, p.fault))):
|
|
raise ValueError('immutable source pose/clock/native/calibration guard')
|
|
previous = p.mono_ns
|
|
pose_ids.add(p.pose_id)
|
|
if poses[-1].mono_ns - poses[0].mono_ns + LAGS_NS[-1] > 600_000_000_000:
|
|
raise ValueError('original maximum 600 second physical history including candidate lookback')
|
|
mapped = {p.pose_id: p for p in poses}
|
|
assigned = []
|
|
block_ids = set()
|
|
previous = 0
|
|
for w in windows:
|
|
if (not isinstance(w, CalibrationWindow) or not _hash(w.block_id) or w.block_id in block_ids or
|
|
not _hash(w.source_window_sha256) or not _integer(w.original_start_ns) or not _integer(w.original_end_ns) or
|
|
not previous < w.original_start_ns <= w.original_end_ns or type(w.pose_ids) is not tuple or
|
|
not 40 <= len(w.pose_ids) <= 240 or any(pid not in mapped for pid in w.pose_ids)):
|
|
raise ValueError('original fixed chronological whole-window declaration')
|
|
selected = [mapped[pid] for pid in w.pose_ids]
|
|
if (tuple(sorted(selected, key=lambda p: p.mono_ns)) != tuple(selected) or
|
|
any(not w.original_start_ns <= p.mono_ns <= w.original_end_ns for p in selected)):
|
|
raise ValueError('pose outside original declared source window/order')
|
|
assigned.extend(w.pose_ids)
|
|
block_ids.add(w.block_id)
|
|
previous = w.original_end_ns
|
|
if assigned != [p.pose_id for p in poses] or len(set(assigned)) != len(assigned):
|
|
raise ValueError('all original poses must be assigned exactly once without dropping/reordering')
|
|
return bound
|
|
|
|
|
|
def _join_all(tape, poses, windows):
|
|
"""Common support uses timestamps/command evidence only, never response y."""
|
|
stamps = [c.apply_ns for c in tape.commands]
|
|
blocks = {pid: w.block_id for w in windows for pid in w.pose_ids}
|
|
joined = {}
|
|
invariants = {} if all(type(p) is SourcePose for p in poses) else None
|
|
for lag in LAGS_NS:
|
|
pairs = []
|
|
previous_command = 0
|
|
for pose_index, p in enumerate(poses):
|
|
_target = p.mono_ns - lag
|
|
index = bisect_right(stamps, p.source_lower_ns-lag) - 1
|
|
upper_index = bisect_right(stamps, p.source_upper_ns-lag) - 1
|
|
if index < 0 or index != upper_index or p.source_upper_ns-lag-stamps[index] > JOIN_NS or stamps[index] <= previous_command:
|
|
# Entire experiment rejects rather than drop this original row at a lag.
|
|
raise ValueError(f'common_command_support:lag={lag}:pose={p.pose_id}')
|
|
c = tape.commands[index]
|
|
# Lazy only after the SAME lag0 endpoint guard. Original max-lag
|
|
# continuity and measured force do not vary with candidate lag. Exact
|
|
# frozen SourcePose only; subclasses retain the original repeated path.
|
|
y = None if invariants is None else invariants.get(pose_index)
|
|
if y is None:
|
|
# A candidate may not bridge unknown/invalid history or original windows.
|
|
first = bisect_right(stamps, p.source_lower_ns - LAGS_NS[-1]) - 1
|
|
last = bisect_right(stamps, p.source_upper_ns) - 1
|
|
if (first < 0 or p.source_lower_ns - LAGS_NS[-1] - stamps[first] > JOIN_NS or last < first or
|
|
any(stamps[i + 1] - stamps[i] > JOIN_NS for i in range(first, last))):
|
|
raise ValueError(f'common_command_continuity:pose={p.pose_id}')
|
|
# Source pose conversion is independent of desired/controller signals.
|
|
y = p.speed_mps * p.calibrated_yaw_rate - 9.81 * math.sin(p.calibrated_roll_rad)
|
|
if not math.isfinite(y):
|
|
raise ValueError('finite measured force response')
|
|
if invariants is not None:
|
|
invariants[pose_index] = y
|
|
pairs.append(Pair(blocks[p.pose_id], p.pose_id, index, c.apply_ns, p.mono_ns, c.command, y))
|
|
previous_command = c.apply_ns
|
|
joined[lag] = tuple(pairs)
|
|
return joined
|
|
|
|
|
|
def _ols(rows):
|
|
n = len(rows)
|
|
mx = sum(r.command for r in rows) / n
|
|
my = sum(r.response for r in rows) / n
|
|
variance = sum((r.command - mx)**2 for r in rows) / n
|
|
if variance < MIN_EXCITATION**2:
|
|
return None
|
|
slope = sum((r.command - mx) * (r.response - my) for r in rows) / (n * variance)
|
|
intercept = my - slope * mx
|
|
if not math.isfinite(slope) or slope <= 0 or not math.isfinite(intercept):
|
|
return None
|
|
return slope, intercept, min(r.command for r in rows), max(r.command for r in rows)
|
|
|
|
|
|
def _score(rows, model):
|
|
slope, intercept, _, _ = model
|
|
ids = tuple(dict.fromkeys(r.block_id for r in rows))
|
|
return sum(sum((r.response - (slope * r.command + intercept))**2 for r in rows if r.block_id == bid) /
|
|
sum(r.block_id == bid for r in rows) for bid in ids) / len(ids)
|
|
|
|
|
|
def _direction_supported(rows):
|
|
for left in (True, False):
|
|
selected = [r for r in rows if (r.command > 0 if left else r.command < 0)]
|
|
if len(selected) < 80:
|
|
return False
|
|
counts = {bid: sum(r.block_id == bid for r in selected) for bid in {r.block_id for r in selected}}
|
|
if sum(n >= 20 for n in counts.values()) < 2:
|
|
return False
|
|
return True
|
|
|
|
|
|
def _pick(training, *, directions=True):
|
|
scores = []
|
|
models = {}
|
|
for lag in LAGS_NS:
|
|
rows = training[lag]
|
|
model = _ols(rows)
|
|
if model is None or directions and not _direction_supported(rows):
|
|
scores.append((lag, None))
|
|
else:
|
|
scores.append((lag, _score(rows, model)))
|
|
models[lag] = model
|
|
valid = sorted((score, lag) for lag, score in scores if score is not None)
|
|
if not valid:
|
|
return None, tuple(scores), 'training_excitation_slope_direction'
|
|
best_score, best = valid[0]
|
|
if len(valid) > 1 and valid[1][0] - best_score <= TIE_ABS_MSE:
|
|
return None, tuple(scores), 'training_lag_ambiguous'
|
|
return (best, models[best]), tuple(scores), ''
|
|
|
|
|
|
def heldout_supported(result):
|
|
"""Keep every heldout error; scope use to the unchanged TRAINING domain.
|
|
|
|
Out-of-domain rows remain in the archive and error score. They neither
|
|
widen the model's command range nor count toward its interpolation support.
|
|
"""
|
|
if len(result.heldout_blocks) != NUM_WINDOWS-TRAIN_WINDOWS or any(
|
|
error > MAX_RMSE for _, error, _ in result.heldout_blocks):
|
|
return False
|
|
if result.support_policy == STRICT_SUPPORT:
|
|
return not any(outside for _, _, outside in result.heldout_blocks)
|
|
if result.support_policy != TRAINING_DOMAIN_ONLY or result.decision is None:
|
|
return False
|
|
decision = result.decision
|
|
ids = tuple(bid for bid, _, _ in result.heldout_blocks)
|
|
if len(set(ids)) != len(ids) or set(ids).intersection(decision.train_block_ids):
|
|
return False
|
|
inside = []
|
|
for bid, error, outside in result.heldout_blocks:
|
|
group = tuple(row for row in result.rows if row.block_id == bid)
|
|
supported = tuple(row for row in group if decision.command_low <= row.command <= decision.command_high)
|
|
if len(supported) < 40 or outside != len(group)-len(supported):
|
|
return False
|
|
# Score ALL original rows, including out-of-domain observations. No
|
|
# response-dependent trimming or favorable validation subset is allowed.
|
|
rmse = math.sqrt(sum((row.response-(decision.slope*row.command+decision.intercept))**2
|
|
for row in group)/len(group))
|
|
if error != rmse:
|
|
return False
|
|
inside.extend(supported)
|
|
return _direction_supported(tuple(inside))
|
|
|
|
|
|
def identify_plant_lag(binding, tape, poses, windows):
|
|
"""Legacy strict-input entrypoint; new physical-only types are rejected."""
|
|
bound = _validate(binding, tape, poses, windows)
|
|
return _identify_validated(binding, tape, poses, windows, bound, protocol=PROTOCOL)
|
|
|
|
|
|
def identify_physical_plant_lag(binding, tape, poses, windows, *, support_policy=STRICT_SUPPORT):
|
|
"""Physical-only observations; never profile, stage or current authority.
|
|
|
|
The child must reconstruct original native/CAN/source proof before building
|
|
this tape. This pure entrypoint verifies custody and unchanged affine gates.
|
|
"""
|
|
bound = _validate_physical(binding, tape, poses, windows)
|
|
return _identify_validated(binding, tape, poses, windows, bound, protocol=PHYSICAL_PROTOCOL, support_policy=support_policy)
|
|
|
|
|
|
def _identify_validated(binding, tape, poses, windows, bound, *, protocol, support_policy=STRICT_SUPPORT):
|
|
"""Shared original all51, train-only lag/affine, heldout/support arithmetic."""
|
|
if support_policy not in (STRICT_SUPPORT, TRAINING_DOMAIN_ONLY) or (support_policy != STRICT_SUPPORT and protocol != PHYSICAL_PROTOCOL):
|
|
raise ValueError('explicit physical response support policy')
|
|
experiment = digest({'protocol': protocol, 'binding': asdict(binding), 'tape': tape.verified_tape_digest,
|
|
'verifier': tape.verifier_source_sha256, 'poses': [asdict(p) for p in poses],
|
|
'windows': [asdict(w) for w in windows], 'grid_ns': LAGS_NS})
|
|
def unavailable(reason, *, decision=None, rows=(), heldout=(), cutoff=None):
|
|
return IdentificationResult('collecting' if decision is None else 'rejected', reason,
|
|
bound, experiment, decision, rows, heldout, None, cutoff)
|
|
try:
|
|
joined = _join_all(tape, poses, windows)
|
|
except ValueError as exc:
|
|
return unavailable(str(exc))
|
|
train_ids = tuple(w.block_id for w in windows[:TRAIN_WINDOWS])
|
|
training = {lag: tuple(r for r in rows if r.block_id in train_ids) for lag, rows in joined.items()}
|
|
picked, scores, reason = _pick(training)
|
|
if picked is None:
|
|
return unavailable(reason)
|
|
lag, model = picked
|
|
block_out = []
|
|
for omitted in train_ids:
|
|
folds = {key: tuple(r for r in rows if r.block_id != omitted) for key, rows in training.items()}
|
|
fold, _, fold_reason = _pick(folds, directions=False)
|
|
if fold is None:
|
|
return unavailable('training_block_out_' + fold_reason)
|
|
block_out.append(fold[0])
|
|
if any(abs(value - lag) > LAG_STABILITY_NS for value in block_out):
|
|
return unavailable('training_lag_block_out_unstable')
|
|
rows = joined[lag]
|
|
train_rows = training[lag]
|
|
train_pose_ids = tuple(r.pose_id for r in train_rows)
|
|
# Bind ONLY the training command data actually visible at/before training end.
|
|
# Heldout witness/response changes cannot alter the locked decision digest.
|
|
train_end = max(r.response_ns for r in train_rows)
|
|
train_source = digest({'binding': asdict(binding),
|
|
'commands': [asdict(c) for c in tape.commands if c.apply_ns <= train_end],
|
|
'poses': [asdict(p) for p in poses if p.pose_id in set(train_pose_ids)],
|
|
'windows': [asdict(w) for w in windows[:TRAIN_WINDOWS]],
|
|
'verifier_source': tape.verifier_source_sha256})
|
|
slope, intercept, low, high = model
|
|
declaration = {'protocol': protocol, 'lag_ns': lag, 'slope': slope, 'intercept': intercept,
|
|
'command_low': low, 'command_high': high, 'train_block_ids': train_ids, 'train_pose_ids': train_pose_ids,
|
|
'train_source_digest': train_source, 'training_pairs_digest': digest([asdict(r) for r in train_rows]),
|
|
'training_scores': scores, 'training_leave_one_out_lags': tuple(block_out)}
|
|
decision = LagDecision(**declaration, digest=digest(declaration))
|
|
# No heldout response has been used by _pick, _ols, folds or declaration.
|
|
heldout = tuple(r for r in rows if r.block_id not in train_ids)
|
|
cutoff = max(r.response_ns for r in rows)
|
|
if not _direction_supported(heldout):
|
|
return unavailable('heldout_direction_support', decision=decision, rows=rows, cutoff=cutoff)
|
|
results = []
|
|
for window in windows[TRAIN_WINDOWS:]:
|
|
group = tuple(r for r in heldout if r.block_id == window.block_id)
|
|
outside = sum(not low <= r.command <= high for r in group)
|
|
rmse = math.sqrt(sum((r.response - (slope * r.command + intercept))**2 for r in group) / len(group))
|
|
results.append((window.block_id, rmse, outside))
|
|
frozen_results = tuple(results)
|
|
if support_policy == STRICT_SUPPORT and any(outside for _, _, outside in results):
|
|
return unavailable('heldout_command_range', decision=decision, rows=rows, heldout=frozen_results, cutoff=cutoff)
|
|
if any(error > MAX_RMSE for _, error, _ in results):
|
|
return unavailable('heldout_rmse', decision=decision, rows=rows, heldout=frozen_results, cutoff=cutoff)
|
|
if support_policy == TRAINING_DOMAIN_ONLY:
|
|
proposed = IdentificationResult('physical_affine_test_passed', 'NO_PROFILE_NO_CURRENT_AUTHORITY', bound,
|
|
experiment, decision, rows, frozen_results, None, cutoff, support_policy)
|
|
if not heldout_supported(proposed):
|
|
return unavailable('heldout_training_domain_support', decision=decision, rows=rows, heldout=frozen_results, cutoff=cutoff)
|
|
model_digest = digest({'support_policy': support_policy, 'protocol': protocol, 'experiment': experiment, 'decision': decision.digest,
|
|
'heldout': frozen_results, 'cutoff_ns': cutoff, 'binding': bound})
|
|
return IdentificationResult('physical_affine_test_passed', 'NO_PROFILE_NO_CURRENT_AUTHORITY', bound,
|
|
experiment, decision, rows, frozen_results, model_digest, cutoff, support_policy)
|