135 lines
4.3 KiB
C
135 lines
4.3 KiB
C
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
/* Copyright (C) 2026 David (Corey) Herrera */
|
|
/*
|
|
* test_qwen35moe.c — Tests for the qwen35moe hybrid architecture.
|
|
*
|
|
* Locks in the A8 oracle validation as a regression guard: the hybrid
|
|
* (30 Gated-Delta SSM + 10 full-attn + shared-expert MoE) forward must
|
|
* load, produce finite logits, and predict the oracle-validated argmax.
|
|
*
|
|
* The forward tests load the real ~16 GB Q3_K_M GGUF, so they are gated
|
|
* behind the PN_QWEN35_MODEL env var (path to the GGUF) — `make test`
|
|
* never loads a model unless you opt in:
|
|
*
|
|
* PN_QWEN35_MODEL=../models/nex-agi_Nex-N2-mini-Q3_K_M.gguf make test
|
|
*
|
|
* Run it through run_model_safe.sh on memory-constrained machines.
|
|
*/
|
|
|
|
#include "lib/test.h"
|
|
#include "tensor/tensor.h"
|
|
#include "../include/pantheon.h"
|
|
#include "../src/core/engine.h"
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
/* Verified config, read from the GGUF metadata. */
|
|
#define Q35_N_LAYER 40
|
|
#define Q35_N_EMBD 2048
|
|
#define Q35_N_VOCAB 248320
|
|
/* A8 oracle result: token "Hello" (9419) at pos 0 → argmax 11. */
|
|
#define Q35_HELLO_TOKEN 9419
|
|
#define Q35_HELLO_ARGMAX 11
|
|
|
|
/* ============================================================
|
|
* Architecture registry (no model needed)
|
|
* ============================================================ */
|
|
|
|
static void test_qwen35moe_forward_fn_registered(void) {
|
|
/* The arch must resolve to a forward fn, else dispatch silently no-ops. */
|
|
ASSERT_NOT_NULL((void *)pn_get_forward_fn(PN_ARCH_QWEN35MOE));
|
|
}
|
|
|
|
/* ============================================================
|
|
* Model-gated integration tests (only run if PN_QWEN35_MODEL is set)
|
|
* ============================================================ */
|
|
|
|
static const char *model_path(void) {
|
|
const char *p = getenv("PN_QWEN35_MODEL");
|
|
if (p && *p && access(p, F_OK) == 0) return p;
|
|
return NULL;
|
|
}
|
|
|
|
static void test_qwen35moe_load_model(void) {
|
|
const char *path = model_path();
|
|
if (!path) { printf(" [SKIP] set PN_QWEN35_MODEL to the GGUF to run\n"); return; }
|
|
|
|
pn_model *model = pn_load(path);
|
|
ASSERT_NOT_NULL(model);
|
|
ASSERT_INT_EQ(PN_ARCH_QWEN35MOE, (int)model->config.arch);
|
|
ASSERT_INT_EQ(Q35_N_LAYER, model->config.n_layer);
|
|
ASSERT_INT_EQ(Q35_N_EMBD, model->config.n_embd);
|
|
ASSERT_INT_EQ(Q35_N_VOCAB, model->config.n_vocab);
|
|
ASSERT_TRUE(model->forward != NULL);
|
|
ASSERT_TRUE(model->tokenizer != NULL); /* BPE + ChatML */
|
|
|
|
pn_model_free(model);
|
|
}
|
|
|
|
static void test_qwen35moe_state_lifecycle(void) {
|
|
const char *path = model_path();
|
|
if (!path) { printf(" [SKIP] set PN_QWEN35_MODEL to the GGUF to run\n"); return; }
|
|
|
|
pn_model *model = pn_load(path);
|
|
ASSERT_NOT_NULL(model);
|
|
|
|
pn_state *state = pn_state_new(model);
|
|
ASSERT_NOT_NULL(state);
|
|
ASSERT_INT_EQ(0, state->pos);
|
|
|
|
pn_state_reset(state);
|
|
ASSERT_INT_EQ(0, state->pos);
|
|
|
|
pn_state_free(state);
|
|
pn_model_free(model);
|
|
}
|
|
|
|
static void test_qwen35moe_forward_matches_oracle(void) {
|
|
const char *path = model_path();
|
|
if (!path) { printf(" [SKIP] set PN_QWEN35_MODEL to the GGUF to run\n"); return; }
|
|
|
|
pn_model *model = pn_load(path);
|
|
ASSERT_NOT_NULL(model);
|
|
pn_state *state = pn_state_new(model);
|
|
ASSERT_NOT_NULL(state);
|
|
|
|
tm_tensor *out = model->forward(model, Q35_HELLO_TOKEN, state);
|
|
ASSERT_NOT_NULL(out);
|
|
ASSERT_INT_EQ(Q35_N_VOCAB, (int)out->shape[0]);
|
|
|
|
float *logits = tm_data_f32(out);
|
|
ASSERT_NOT_NULL(logits);
|
|
|
|
/* Logits finite and argmax matches the llama.cpp oracle (A8). */
|
|
int best = 0;
|
|
int finite = 1;
|
|
for (int i = 0; i < Q35_N_VOCAB; i++) {
|
|
if (!isfinite(logits[i])) { finite = 0; break; }
|
|
if (logits[i] > logits[best]) best = i;
|
|
}
|
|
ASSERT_TRUE(finite);
|
|
ASSERT_INT_EQ(Q35_HELLO_ARGMAX, best);
|
|
|
|
tm_free(out);
|
|
pn_state_free(state);
|
|
pn_model_free(model);
|
|
}
|
|
|
|
int main(void) {
|
|
printf("Pantheon qwen35moe Tests\n");
|
|
printf("========================\n\n");
|
|
|
|
/* Registry (no model needed) */
|
|
RUN_TEST(qwen35moe_forward_fn_registered);
|
|
|
|
/* Model-gated integration (PN_QWEN35_MODEL) */
|
|
RUN_TEST(qwen35moe_load_model);
|
|
RUN_TEST(qwen35moe_state_lifecycle);
|
|
RUN_TEST(qwen35moe_forward_matches_oracle);
|
|
|
|
TEST_REPORT();
|
|
return __ft_failed > 0 ? 1 : 0;
|
|
}
|