Pantheon/tests/parity_accel_arm.h

104 lines
5.1 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Copyright (C) 2026 David (Corey) Herrera */
/**
* @file parity_accel_arm.h
* @brief Pick the accelerator arm a parity harness should score, and refuse the vacuous
* CPU-vs-CPU configuration — shared by every harness so the identity control means
* the same thing in all of them.
*
* @status Working
* @issues None
* @todo None
*/
#ifndef PN_TESTS_PARITY_ACCEL_ARM_H
#define PN_TESTS_PARITY_ACCEL_ARM_H
#include "../include/pantheon.h"
#include "../src/core/engine.h"
/**
* Return WHICHEVER forward the engine selected for `model`, not a hardcoded one.
*
* Hardcoding pn_llama_forward_cuda made a harness silently CPU-vs-CPU on any other card — a
* green run that proves nothing, which is the failure mode this project keeps paying for.
*
* PN_PARITY_IDENTITY=1 deliberately makes the arm the CPU oracle itself. That is the one
* legitimate CPU-vs-CPU configuration: every reported difference must come out exactly zero,
* and if it does not, no accelerator number from the harness can be believed. It says so on
* stderr so a stray environment variable can never be mistaken for a passing card run.
*
* The rule is an IDENTITY, not a table: the accelerator arm is `model->forward` whenever it
* differs from the architecture's own CPU forward. A table of known accelerator symbols has
* to be extended for every family, and the one that was here listed only the llama pair — so
* on a qwen35moe model (Ornith) it selected nothing and the caller refused, which is the same
* blindness in a politer form. The identity rule cannot go stale, and it is also the only
* rule that gets Bonsai right: that architecture's CPU forward is pn_bonsai_forward while its
* TT forward is pn_qwen35moe_forward_tt, a pair no per-family table would have written down.
*
* @param name_out set to a human name for the arm ("TT", "CUDA", "CPU-IDENTITY-CONTROL")
* @return the forward to score, or NULL when there is no accelerator and no
* identity control — in which case the caller must refuse to run
*
* Thread-safe: yes Reentrant: yes Signal-safe: no
*/
pn_forward_fn parity_select_accel_arm(pn_model *model, const char **name_out);
/**
* The CPU oracle every parity harness scores AGAINST, for `model`'s own architecture.
*
* It comes from `pn_get_forward_fn(model->config.arch)` — the engine's own architecture
* registry — rather than from a second copy of that mapping, so the oracle cannot drift from
* what the engine calls the CPU path. Harnesses used to hardcode `pn_llama_forward` here,
* which on a qwen35moe model would have scored a card against a DIFFERENT MODEL's forward
* reading a qwen35moe weight struct.
*
* @param name_out set to a human name for the oracle, e.g. "CPU/qwen35moe"
* @return the CPU forward, or NULL when the architecture has none
*
* Thread-safe: yes Reentrant: yes Signal-safe: no
*/
pn_forward_fn parity_select_cpu_oracle(pn_model *model, const char **name_out);
/**
* Refuse a model whose CPU oracle is not `expected`.
*
* The arm selection above is deliberately architecture-agnostic, but some harnesses' PROBES
* are not — a residual-divergence probe casts `model->weights` to `pn_llama_qweights *`, which
* on any other family is a wrong-struct read, and it used to be protected only by the arm
* table happening to list llama alone. Making selection general moved that guard out from
* under them, so it is stated here instead of being an accident of a table.
*
* @param tool the harness's name, for the refusal message
* @return 0 when the oracle matches, -1 when the caller must not run
*
* Thread-safe: yes Reentrant: yes Signal-safe: no
*/
int parity_require_cpu_oracle(pn_model *model, pn_forward_fn expected, const char *tool);
/**
* Detach or reattach whatever accelerator SEAM this model's family installs on shared state,
* so the CPU oracle is genuinely the CPU.
*
* **NOT EVERY ACCELERATOR IS A FORWARD.** The llama family's is: `pn_llama_forward_tt` is a
* different function and calling `pn_llama_forward` runs on the host. The qwen35/bonsai
* family's is a HOOK — `qwen35moe_mv()` consults `cfg->mv.fn` on every projection and the
* upload installs the card's there — so in a card-armed process the architecture's "CPU
* forward" still runs all 249 projections on the card. A harness that scored the two forwards
* against each other there would be comparing the card with the card, and against the
* RESIDENT-ACTIVATION arm it segfaults instead, which is how this was found.
*
* Families with no seam are a no-op, so a caller may bracket every oracle call with this
* unconditionally.
*
* @param enable 0 before running the CPU oracle, non-zero before running the accelerator
* @return 1 if a seam was attached before the call, 0 if not
*
* Thread-safe: no Reentrant: no Signal-safe: no
*/
int parity_accel_seam_enable(pn_model *model, int enable);
/** @return non-zero when PN_PARITY_IDENTITY selected the identity control. */
int parity_identity_control_active(void);
#endif /* PN_TESTS_PARITY_ACCEL_ARM_H */