Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
441 changes: 441 additions & 0 deletions docs/design/p3-router-validation-delivery.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions rl_engine/moe/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 RL-Kernel Contributors

"""P3 router package (DSV4-Flash MoE router, contract `p3-task-selection.md`).

Layout sanctioned by the P3 contract §2.1:
- ``naive_topk6`` : T09-owned total-order Top-6 checker (cross-checks T01 golden)
- ``router_torch_reference.py`` : T01-owned raw Torch reference (to be published
with the start kit; do not duplicate or silently replace it)
"""
163 changes: 163 additions & 0 deletions rl_engine/moe/naive_topk6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 RL-Kernel Contributors

"""T09-owned naive total-order Top-6 (contract §2.1 / §4-T09 / fixture table §3.3).

Purpose
-------
This is the *independent* re-implementation of stable Top-6 used **only** to
cross-check T01's ``stable_topk6`` golden. It is deliberately written in the
most naive way possible — a full total-order ``sorted()`` over all E experts —
so that any race, tie-mishandling or post-tie reorder inside faster
implementations shows up as a diff here.

Ownership (contract §3.3): ``naive total-order Top-6`` is owned by **T09** and
consumed by **T01**. It must NOT be used as a production selection kernel.

Semantics frozen by the contract
--------------------------------
* Order key: ``(q descending, logical_expert_id ascending)`` (§2.6: the P3
canonical tie-break when Miles has no explicit deterministic tie).
* Slot order: slots 0..5 keep the order produced by the total sort; per §2.1
Learned selection, ``ids = stable_topk6(q)`` with q descending and
logical_expert_id ascending — six table slots keep original order, no
re-sort / re-topk / reorder afterwards (that rule is stated for Hash but the
same slot-order guarantee applies to the Top-6 output consumed by T04).
* Ties: a full total order handles exact ties naturally; near-ties (distinct
FP32 q values that differ only in low bits) must still sort by value first.
* Padding rows are not selected here — the caller (assembler) owns padding
canonicalization; this module never sees padding rows.

This module is CPU/FP32 pure-Python by design (auditability over speed): it
operates on Python floats converted from FP32 so the ordering decisions are
made on the exact FP32 bit patterns, not on double-precision artefacts.
"""

from __future__ import annotations

from typing import NamedTuple

import torch

K: int = 6 # contract §2.1: K=6 fixed for DSV4-Flash
DEFAULT_E: int = 256 # contract §2.1: E=256


class NaiveTopk6Result(NamedTuple):
"""Total-order Top-6 output for one row.

Attributes:
ids: ``INT32 [K]`` logical expert ids in canonical order
(q desc, logical_expert_id asc).
values: ``FP32 [K]`` the q values corresponding to ``ids``.
"""

ids: list[int]
values: list[float]


def _fp32_key(value: float) -> tuple[int, float]:
"""Return a sort key that orders FP32 values exactly as FP32 compares.

Python floats are doubles; two distinct FP32 values remain distinct and
correctly ordered as doubles (FP32 -> double is exact), so a plain
descending value sort is already faithful to FP32 semantics. We keep this
helper to make the intent explicit and to give a single place to audit.
"""
return (0, value)


def naive_topk6_row(q_row: torch.Tensor) -> NaiveTopk6Result:
"""Total-order Top-6 for a single row of q (FP32 ``[E]``)."""
if q_row.ndim != 1:
raise ValueError(f"q_row must be 1-D, got shape {tuple(q_row.shape)}")
if q_row.numel() == 0:
raise ValueError("q_row must be non-empty")
if q_row.dtype != torch.float32:
raise ValueError(f"q_row must be FP32, got {q_row.dtype}")

# Work on exact FP32 values pulled out as doubles (lossless widening).
values = q_row.tolist()
e = len(values)
if e < K:
raise ValueError(f"need at least K={K} experts, got E={e}")

# Total order: (q desc, logical_expert_id asc). A single sorted() pass over
# (value, id) pairs with the id as the ascending tie-break achieves this.
order = sorted(range(e), key=lambda i: (-values[i], i))
top = order[:K]
return NaiveTopk6Result(
ids=[int(i) for i in top],
values=[float(values[i]) for i in top],
)


def naive_topk6(q: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
"""Batch total-order Top-6.

Args:
q: FP32 ``[T, E]`` selection scores (``q = s + correction_bias``).

Returns:
``(ids, top_values)`` where ``ids`` is INT32 ``[T, K]`` and
``top_values`` is FP32 ``[T, K]``, both in canonical order
(q descending, logical_expert_id ascending), slots keep sort order.
"""
if q.ndim != 2:
raise ValueError(f"q must be 2-D [T,E], got shape {tuple(q.shape)}")
if q.dtype != torch.float32:
raise ValueError(f"q must be FP32, got {q.dtype}")

t, e = q.shape
if e < K:
raise ValueError(f"need at least K={K} experts, got E={e}")

rows = q.tolist() # exact FP32 -> double widening per element
ids = torch.empty((t, K), dtype=torch.int32)
top_values = torch.empty((t, K), dtype=torch.float32)
for r, row in enumerate(rows):
order = sorted(range(e), key=lambda i: (-row[i], i))
for slot, idx in enumerate(order[:K]):
ids[r, slot] = idx
top_values[r, slot] = row[idx]
return ids, top_values


def cross_check_topk6(
candidate_ids: torch.Tensor,
q: torch.Tensor,
) -> tuple[bool, str]:
"""Cross-check a candidate Top-6 implementation against the naive order.

This is the T09 -> T01 cross-check entry point: T01 runs its
``stable_topk6`` fixtures (random / near-tie / exact-tie) through this
checker; any mismatch is a defect in the candidate, never in this module.

Args:
candidate_ids: INT32 ``[T, K]`` ids produced by the implementation
under test (slot order must be its own output order).
q: FP32 ``[T, E]`` the exact scores the candidate was invoked with.

Returns:
``(passed, message)``; ``message`` pinpoints the first mismatching
``(row, slot)`` with both id sequences — first-mismatch style, per
contract §4-T09 (first mismatch must be locatable, not averaged away).
"""
if candidate_ids.shape != q.shape[:-1] + (K,):
return False, (
f"shape mismatch: candidate_ids {tuple(candidate_ids.shape)} "
f"vs expected {tuple(q.shape[:-1])}x{K}"
)

expected_ids, _ = naive_topk6(q)
t = q.shape[0]
cand = candidate_ids.to(torch.int64).tolist()
exp = expected_ids.to(torch.int64).tolist()
for r in range(t):
if cand[r] != exp[r]:
slot = next(i for i in (range(K)) if cand[r][i] != exp[r][i])
return False, (
f"first mismatch at (row={r}, slot={slot}): "
f"candidate={cand[r]} expected={exp[r]}"
)
return True, "ok"
151 changes: 151 additions & 0 deletions rl_engine/moe/p3_verdicts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 RL-Kernel Contributors

"""P3 unified fail-closed verdict codes (contract §6, frozen).

Rules frozen by the contract:
- ``P3Verdict`` is INT32. Device may write only 1-2 (3-9 reserved), provider
10-22, runner 50-72, certification 90-99 reserved. New codes can only be
appended, never re-ordered.
- ``PASS(0)`` is a single-operator verdict; ``CASE_PASS(50)`` is case-level.
The two must not be conflated.
- Operator APIs never return runner/certification codes; only the case runner
may produce ``CASE_PASS``.
- Multi-error cases pick the primary verdict by fixed priority:
infrastructure integrity / identity / schema -> upstream evidence ->
discrete plan -> numeric bytes -> fingerprint -> diagnostics. The runner
must not rewrite this priority.
"""

from __future__ import annotations

from enum import IntEnum


class P3Verdict(IntEnum):
"""Unified fail-closed verdict codes (contract §6)."""

# --- provider layer (operator verdicts) ---
PASS = 0 # launch/readback/echo/status all clean
# device-writable (kernel may only atomicMin these two)
NON_FINITE = 1 # P3's own computation produced non-finite
HASH_TABLE_INDEX_OUT_OF_RANGE = 2
# provider layer
IDENTITY_DRIFT = 10
SCHEMA_MISMATCH = 11
CORRUPT_ARTIFACT = 12
INCOMPLETE_ARTIFACT = 13
LOGIT_ROUND_POINT_MISMATCH = 14
HASH_TABLE_MISMATCH = 15
UNSUPPORTED_CAPABILITY = 16
ZERO_ACTIVE_TOKENS = 17
UPSTREAM_NON_FINITE = 18
GATE_SHARDING_MISMATCH = 19
MISSING_RANK = 20
PRE_UPDATE_WEIGHT_DRIFT = 21
STALE_RUN_METADATA = 22

# --- runner layer (case verdicts) ---
CASE_PASS = 50
ROUTE_WEIGHT_BYTES_MISMATCH = 51
SCORE_BYTES_MISMATCH = 52
GRADIENT_BYTES_MISMATCH = 53
BYTE_MISMATCH = 54
TOPK_ORDER_MISMATCH = 55
TIE_BREAK_POLICY_MISMATCH = 56
INVALID_DISCRETE_PLAN = 57
INVALID_PROFILE = 58
ROUTE_SEMANTIC_FINGERPRINT_MISMATCH = 59
ROUTE_ARTIFACT_FINGERPRINT_MISMATCH = 60
SELECTION_GRADIENT_PRESENT = 61
FORBIDDEN_LOCAL_SHARD_TOPK = 62
AMBIGUOUS_GLOBAL_TOKEN_MAPPING = 63
INVALID_PLACEMENT_MAP = 64
PLACEMENT_MAP_VERSION_MISMATCH = 65
SILENT_FALLBACK = 66
MISSING_PROVENANCE = 67
MISSING_BOUNDARY_TRACE = 68
UPSTREAM_CONTRACT_MISMATCH = 69
UPSTREAM_VERDICT_MISSING = 70
UPSTREAM_EVIDENCE_MISSING = 71
NATURAL_ROUTE_MISMATCH = 72


#: Device-writable status values (kernel may only write these via atomicMin).
DEVICE_WRITABLE = frozenset({P3Verdict.NON_FINITE, P3Verdict.HASH_TABLE_INDEX_OUT_OF_RANGE})

#: Values reserved for future device use (3-9); anything else written by a
#: kernel is CORRUPT_ARTIFACT per contract §6.
DEVICE_RESERVED_RANGE = range(3, 10)

#: Provider-writable band.
PROVIDER_RANGE = range(10, 23)

#: Runner-writable band.
RUNNER_RANGE = range(50, 73)


def is_valid_device_status(value: int) -> bool:
"""True iff a kernel-written device status value is legal (1, 2)."""
try:
return P3Verdict(value) in DEVICE_WRITABLE
except ValueError:
return False


def classify_writable_band(value: int) -> str:
"""Return which layer owns ``value``; used to police layer violations."""
if value == 0 or value in DEVICE_WRITABLE:
return "device-or-provider"
if value in PROVIDER_RANGE:
return "provider"
if value in RUNNER_RANGE:
return "runner"
return "reserved"


def primary_verdict(verdicts: list[P3Verdict]) -> P3Verdict | None:
"""Pick the primary verdict among multiple failures (contract §6 order).

Fixed priority: infrastructure integrity / identity / schema ->
upstream evidence -> discrete plan -> numeric bytes -> fingerprint ->
diagnostics. Implementation: explicit rank map, stable for unknown codes
(they rank last, preserving input order via ``sorted`` stability).
"""
if not verdicts:
return None

rank: dict[P3Verdict, int] = {}
order = [
# infrastructure integrity / identity / schema
[P3Verdict.CORRUPT_ARTIFACT, P3Verdict.IDENTITY_DRIFT, P3Verdict.SCHEMA_MISMATCH,
P3Verdict.STALE_RUN_METADATA, P3Verdict.INCOMPLETE_ARTIFACT],
# upstream evidence
[P3Verdict.UPSTREAM_VERDICT_MISSING, P3Verdict.UPSTREAM_EVIDENCE_MISSING,
P3Verdict.UPSTREAM_CONTRACT_MISMATCH, P3Verdict.UPSTREAM_NON_FINITE,
P3Verdict.MISSING_PROVENANCE, P3Verdict.MISSING_BOUNDARY_TRACE,
P3Verdict.MISSING_RANK],
# discrete plan
[P3Verdict.INVALID_DISCRETE_PLAN, P3Verdict.TOPK_ORDER_MISMATCH,
P3Verdict.TIE_BREAK_POLICY_MISMATCH, P3Verdict.FORBIDDEN_LOCAL_SHARD_TOPK,
P3Verdict.AMBIGUOUS_GLOBAL_TOKEN_MAPPING, P3Verdict.INVALID_PLACEMENT_MAP,
P3Verdict.PLACEMENT_MAP_VERSION_MISMATCH, P3Verdict.HASH_TABLE_MISMATCH,
P3Verdict.LOGIT_ROUND_POINT_MISMATCH, P3Verdict.GATE_SHARDING_MISMATCH,
P3Verdict.INVALID_PROFILE],
# numeric bytes
[P3Verdict.ROUTE_WEIGHT_BYTES_MISMATCH, P3Verdict.SCORE_BYTES_MISMATCH,
P3Verdict.GRADIENT_BYTES_MISMATCH, P3Verdict.BYTE_MISMATCH,
P3Verdict.SELECTION_GRADIENT_PRESENT, P3Verdict.SILENT_FALLBACK,
P3Verdict.NON_FINITE, P3Verdict.HASH_TABLE_INDEX_OUT_OF_RANGE],
# fingerprint
[P3Verdict.ROUTE_SEMANTIC_FINGERPRINT_MISMATCH,
P3Verdict.ROUTE_ARTIFACT_FINGERPRINT_MISMATCH,
P3Verdict.PRE_UPDATE_WEIGHT_DRIFT],
# diagnostics
[P3Verdict.NATURAL_ROUTE_MISMATCH],
]
for group_rank, group in enumerate(order):
for v in group:
rank[v] = group_rank

return sorted(verdicts, key=lambda v: rank.get(v, len(order)))[0]
12 changes: 12 additions & 0 deletions rl_engine/moe/validation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 RL-Kernel Contributors

"""T09 comparison engine for P3 router validation (contract §2.5 / §4-T09).

Submodules:
- ``first_mismatch`` : locate the first divergence between two event streams
by the six-tuple ``(absolute_layer, site, pass, event_index,
global_token_id, rank)`` and attribute it to an owner task / issue.
- ``comparison`` : the four-stage ordered comparator
(identity -> discrete -> score/weight -> gradient) with fail-closed gates.
"""
Loading