ProprietaryLegal's picture
PLI Labs public snapshot 2026-10-28 (history squashed)
e1acc15
Raw History Blame Contribute Delete
8.1 kB
"""PP4's verifier under multi-round sampled drafting at T=1, on CPU (R-PP4: the #61161 check on PP4's own path).
The kernel is the image's rejection_sampler_utils.py transformed by PP3's pinned prepare_verifier.utilities, which is
exactly the file PP4's overlay ships (pp4_prepare writes the same text): full-vocabulary FP32 probabilities q passed as
draft_logits (HAS_DRAFT_LOGITS == 2), standard verification only. Each trial drafts from a context-dependent proposal q
that differs from the target, samples every draft from q with a random stream owned by its round (never keyed by
position, the rule #61161 states for revisited-position draft noise), passes q's rows and commits exactly as the V2
model runner does (multiround.py). Speculative sampling is exact only if the generated tokens follow the target's law;
#61161's reuse would also show as re-read acceptance keys. Synthetic numbers only.
"""
from __future__ import annotations
import hashlib
import multiprocessing as mp
import os
import random
import sys
from dataclasses import dataclass
from pathlib import Path
import torch
_HERE = Path(__file__).resolve().parent
_ROOT = _HERE.parents[2]
for _path in (str(_HERE), str(_ROOT)):
if _path not in sys.path:
sys.path.insert(0, _path)
import multiround as mr
from kernel_loader import DEFAULT_SRC, load
SAMPLED_ENV = "VLLM_DSPARK_SAMPLED_TOP32"
@dataclass(frozen=True)
class SampledCase:
"""Target law and window/depth policy from `toy` (its draft_next is unused); drafts are drawn from `sample_q`."""
toy: mr.Toy
q: tuple[tuple[float, ...], ...] # proposal row passed to the verifier: previous token -> q over the vocab
sample_q: tuple[tuple[float, ...], ...] | None = None # negative control only: draw from a different law than q
_TARGET = ((0.5, 0.3, 0.2), (0.2, 0.5, 0.3), (0.3, 0.2, 0.5))
# q differs from the target on every row and gives token 2 zero mass after token 0 (disjoint support: log q = -inf).
_Q = ((0.7, 0.3, 0.0), (0.1, 0.6, 0.3), (0.2, 0.2, 0.6))
# Production-shaped: context-dependent target, sampled drafter, windows 1..5 and depth dead rows from committed history.
PP4_CASE = SampledCase(mr.Toy(target=_TARGET, draft_next=(0, 0, 0), max_k=5, length=4), q=_Q)
# PP4's measured configuration: fixed depth 5, every row valid.
PP4_FIXED5 = SampledCase(mr.Toy(target=_TARGET, draft_next=(0, 0, 0), max_k=5, length=4,
vary_window=False, dead_rows=False), q=_Q)
# Negative control: the verifier is told q but the drafts come from another law, which must bias the output.
MISMATCH = SampledCase(PP4_FIXED5.toy, q=_Q, sample_q=((0.1, 0.1, 0.8), (0.8, 0.1, 0.1), (0.1, 0.8, 0.1)))
def write_pp4_kernel(dest: Path, src_root: Path = DEFAULT_SRC) -> Path:
"""The PP4 verifier kernel, generated from the pinned image source by PP3's reviewed transformation."""
from offload.dspark.probabilistic.pp3 import prepare_verifier as pp3
data = (src_root / pp3.UTILS).read_bytes()
if hashlib.sha256(data).hexdigest() != pp3.PINS[pp3.UTILS]:
raise RuntimeError("image kernel differs from PP3's pinned source")
out = dest / "rejection_sampler_utils_pp4.py"
out.write_text(pp3.utilities(data.decode()))
return out
def _run_chunk(args: tuple[SampledCase, int, int, str]) -> mr.Result:
case, n, chunk_seed, kernel = args
os.environ[SAMPLED_ENV] = "1"
module = load(Path(kernel))
import triton.language as tl # type: ignore[import-untyped]
class _InterpreterDevice:
# Triton's CPU interpreter has no libdevice (its log1p returns None). The q residual's only libdevice call,
# log1p(-ratio), becomes log(1 - ratio): the same law at CPU rounding (GPU rounding is not part of the claim).
@staticmethod
def log1p(x: tl.tensor) -> tl.tensor:
return tl.log(1.0 + x)
module.tldevice = _InterpreterDevice()
toy = case.toy
draw_q = case.sample_q or case.q
rng = random.Random(chunk_seed)
seeds = [rng.getrandbits(63) - (1 << 62) for _ in range(n)]
log_target = torch.tensor(toy.target, dtype=torch.float32).log()
q_rows = torch.tensor(case.q, dtype=torch.float32)
seqs: list[list[int]] = [[toy.prompt_token] for _ in range(n)]
consulted: list[set[tuple[int, int]]] = [set() for _ in range(n)]
res = mr.Result(trials=n)
rnd = 0
while True:
active = [r for r in range(n) if len(seqs[r]) - 1 < toy.length]
if not active:
break
rnd += 1
nb = len(active)
q = torch.zeros(nb, toy.max_k, toy.vocab, dtype=torch.float32)
logits_rows: list[torch.Tensor] = []
draft_sampled: list[int] = []
pos: list[int] = []
cu = [0]
local_pos: list[int] = []
expanded: list[int] = []
for b, r in enumerate(active):
seq = seqs[r]
k, d = mr._window(toy, seq, rnd)
start = toy.prompt_pos + len(seq) - 1
# One draft stream per (request, round): a later round re-drafts a revisited position from new randomness.
stream = random.Random(f"{seeds[r]}:{rnd}")
row_tokens = [seq[-1]]
for i in range(k):
prev = row_tokens[-1]
q[b, i] = q_rows[prev]
row_tokens.append(stream.choices(range(toy.vocab), weights=draw_q[prev])[0])
for i, tok in enumerate(row_tokens):
logits_rows.append(log_target[tok])
draft_sampled.append(-1 if i > d else tok)
pos.append(start + i)
local_pos.append(i)
expanded.append(b)
cu.append(cu[-1] + k + 1)
sampled, num_sampled = module.rejection_sample(
torch.stack(logits_rows).contiguous(),
q, # PP4: full-vocabulary FP32 q rows, HAS_DRAFT_LOGITS == 2
torch.tensor(draft_sampled, dtype=torch.int64),
torch.tensor(cu, dtype=torch.int32),
torch.tensor(pos, dtype=torch.int64),
torch.arange(nb, dtype=torch.int32),
torch.tensor(expanded, dtype=torch.int32),
torch.tensor(local_pos, dtype=torch.int32),
torch.ones(nb, dtype=torch.float32), # T=1
torch.tensor([seeds[r] for r in active], dtype=torch.int64),
toy.max_k,
use_block_verification=False,
)
for b, r in enumerate(active):
m = int(num_sampled[b])
k = cu[b + 1] - cu[b] - 1
start = pos[cu[b]]
# Standard verification reads row i's acceptance uniform, keyed (seed, start + i), only while verifying.
valid = 0
while valid < k and draft_sampled[cu[b] + valid + 1] >= 0:
valid += 1
for i in range(min(m, valid)):
key = (0, start + i)
if key in consulted[r]:
res.reread_keys += 1
consulted[r].add(key)
seqs[r].extend(int(t) for t in sampled[b, :m])
res.accepted += m - 1
committed_upto = start + m - 1
consulted[r] = {c for c in consulted[r] if c[1] > committed_upto}
res.rounds += 1
for seq in seqs:
gen = tuple(seq[1 : 1 + toy.length])
res.counts[gen] = res.counts.get(gen, 0) + 1
return res
def simulate(case: SampledCase, trials: int, seed: int, kernel: Path, workers: int = 8) -> mr.Result:
if os.environ.get("TRITON_INTERPRET") != "1" or os.environ.get("CUDA_VISIBLE_DEVICES") != "":
raise RuntimeError("TRITON_INTERPRET=1 and CUDA_VISIBLE_DEVICES= are required")
chunks = max(1, min(workers, trials))
sizes = [trials // chunks + (1 if i < trials % chunks else 0) for i in range(chunks)]
jobs = [(case, s, seed * 1000 + i, str(kernel)) for i, s in enumerate(sizes) if s]
total = mr.Result()
if len(jobs) == 1:
total.merge(_run_chunk(jobs[0]))
return total
with mp.get_context("spawn").Pool(len(jobs)) as pool:
for part in pool.map(_run_chunk, jobs):
total.merge(part)
return total