/* SPDX-License-Identifier: AGPL-3.0-or-later */ /* Copyright (C) 2026 David (Corey) Herrera */ /** * @file test_sampling_modifiers.c * @brief Unit tests for the OpenAI logits-row sampling controls * (src/core/sampling_modifiers.c): `logit_bias`, * `presence_penalty`/`frequency_penalty`, the seeded `seed` RNG, and * `logprobs`/`top_logprobs` extraction. * * These assert on the ARITHMETIC and the ORDERING, not on a generated string — * the bug class this area already shipped once was an ordering bug (a penalty * applied on the wrong side of the greedy branch), which no end-to-end text * check would have localized. * * @status Complete for the four fields implemented. * @issues None * @todo None */ #include #include #include #include "lib/test.h" #include "../src/core/sampling_modifiers.h" #define VOCAB 8 static void reset(float *logits) { for (int i = 0; i < VOCAB; i++) logits[i] = 0.0f; } /* ── logit_bias ── */ static void test_logit_bias_adds_to_the_named_tokens(void) { float logits[VOCAB]; reset(logits); logits[3] = 1.0f; const pn_logit_bias_entry bias[] = { { 3, -5.0f }, { 5, 2.5f } }; pn_apply_logit_bias(logits, VOCAB, bias, 2); ASSERT_FLT_NEAR(logits[3], -4.0f, 1e-6f); ASSERT_FLT_NEAR(logits[5], 2.5f, 1e-6f); ASSERT_FLT_NEAR(logits[0], 0.0f, 1e-6f); /* untouched */ } static void test_logit_bias_ignores_out_of_range_ids(void) { float logits[VOCAB]; reset(logits); const pn_logit_bias_entry bias[] = { { VOCAB, 100.0f }, { 999999, -100.0f } }; pn_apply_logit_bias(logits, VOCAB, bias, 2); for (int i = 0; i < VOCAB; i++) ASSERT_FLT_NEAR(logits[i], 0.0f, 1e-6f); } static void test_logit_bias_off_is_a_noop(void) { float logits[VOCAB]; reset(logits); logits[2] = 7.0f; pn_apply_logit_bias(logits, VOCAB, NULL, 0); ASSERT_FLT_NEAR(logits[2], 7.0f, 1e-6f); } /* ── presence / frequency penalties ── */ static void test_frequency_penalty_scales_with_count(void) { float logits[VOCAB]; reset(logits); /* token 4 appears three times, token 6 once. */ const uint32_t recent[] = { 4, 6, 4, 4 }; pn_apply_presence_frequency_penalties(logits, VOCAB, 0.0f, 1.0f, recent, 4); ASSERT_FLT_NEAR(logits[4], -3.0f, 1e-6f); ASSERT_FLT_NEAR(logits[6], -1.0f, 1e-6f); ASSERT_FLT_NEAR(logits[0], 0.0f, 1e-6f); } static void test_presence_penalty_is_charged_once(void) { float logits[VOCAB]; reset(logits); const uint32_t recent[] = { 4, 4, 4 }; pn_apply_presence_frequency_penalties(logits, VOCAB, 2.0f, 0.0f, recent, 3); ASSERT_FLT_NEAR(logits[4], -2.0f, 1e-6f); /* NOT -6 */ } static void test_both_penalties_compose(void) { float logits[VOCAB]; reset(logits); const uint32_t recent[] = { 1, 1 }; pn_apply_presence_frequency_penalties(logits, VOCAB, 0.5f, 0.25f, recent, 2); /* 2 occurrences * 0.25 frequency + 1 * 0.5 presence */ ASSERT_FLT_NEAR(logits[1], -1.0f, 1e-6f); } static void test_penalties_off_is_a_noop(void) { float logits[VOCAB]; reset(logits); logits[1] = 3.0f; const uint32_t recent[] = { 1, 1 }; pn_apply_presence_frequency_penalties(logits, VOCAB, 0.0f, 0.0f, recent, 2); ASSERT_FLT_NEAR(logits[1], 3.0f, 1e-6f); /* …and an empty window is a no-op even with penalties set. */ pn_apply_presence_frequency_penalties(logits, VOCAB, 1.0f, 1.0f, NULL, 0); ASSERT_FLT_NEAR(logits[1], 3.0f, 1e-6f); } static void test_negative_penalty_boosts(void) { /* OpenAI allows -2.0..2.0; a negative value must ENCOURAGE repetition. */ float logits[VOCAB]; reset(logits); const uint32_t recent[] = { 2 }; pn_apply_presence_frequency_penalties(logits, VOCAB, -1.0f, 0.0f, recent, 1); ASSERT_FLT_NEAR(logits[2], 1.0f, 1e-6f); } /* ── seeded RNG ── */ static void test_same_seed_gives_the_same_stream(void) { uint64_t a = pn_seed_to_rng_state(42); uint64_t b = pn_seed_to_rng_state(42); for (int i = 0; i < 64; i++) { float x = pn_seeded_rng_next_float(&a); float y = pn_seeded_rng_next_float(&b); ASSERT_FLT_NEAR(x, y, 0.0f); } } static void test_different_seeds_diverge_immediately(void) { /* Neighbouring small seeds are the common case (0, 1, 42) — they must not * produce a correlated FIRST draw, which is what the splitmix64 diffuse in * pn_seed_to_rng_state is for. */ uint64_t a = pn_seed_to_rng_state(0); uint64_t b = pn_seed_to_rng_state(1); float x = pn_seeded_rng_next_float(&a); float y = pn_seeded_rng_next_float(&b); ASSERT_TRUE(fabsf(x - y) > 0.01f); } static void test_seed_zero_is_not_a_degenerate_state(void) { /* 0 is xorshift's fixed point: an undiffused seed of 0 would return 0.0 * forever and pin every draw to token 0. */ uint64_t s = pn_seed_to_rng_state(0); ASSERT_TRUE(s != 0); int nonzero = 0; for (int i = 0; i < 32; i++) if (pn_seeded_rng_next_float(&s) > 0.0f) nonzero++; ASSERT_TRUE(nonzero > 25); } static void test_rng_stays_in_unit_interval(void) { uint64_t s = pn_seed_to_rng_state(12345); for (int i = 0; i < 4096; i++) { float v = pn_seeded_rng_next_float(&s); ASSERT_TRUE(v >= 0.0f); ASSERT_TRUE(v < 1.0f); } } /* ── logprobs ── */ static void test_logprobs_sum_to_one(void) { float logits[VOCAB]; for (int i = 0; i < VOCAB; i++) logits[i] = (float)i * 0.5f; uint32_t ids[PN_LOGPROBS_MAX_TOP]; float lps[PN_LOGPROBS_MAX_TOP]; float chosen = 0.0f; int n = pn_logprobs_extract(logits, VOCAB, 3, VOCAB, &chosen, ids, lps); ASSERT_INT_EQ(n, VOCAB); float total = 0.0f; for (int i = 0; i < n; i++) total += expf(lps[i]); ASSERT_FLT_NEAR(total, 1.0f, 1e-4f); /* Every logprob is <= 0 (a probability). */ for (int i = 0; i < n; i++) ASSERT_TRUE(lps[i] <= 1e-6f); } static void test_logprobs_are_descending_and_top_is_argmax(void) { float logits[VOCAB]; reset(logits); logits[5] = 10.0f; logits[2] = 9.0f; logits[7] = 8.0f; uint32_t ids[PN_LOGPROBS_MAX_TOP]; float lps[PN_LOGPROBS_MAX_TOP]; int n = pn_logprobs_extract(logits, VOCAB, 5, 3, NULL, ids, lps); ASSERT_INT_EQ(n, 3); ASSERT_INT_EQ((int)ids[0], 5); ASSERT_INT_EQ((int)ids[1], 2); ASSERT_INT_EQ((int)ids[2], 7); ASSERT_TRUE(lps[0] > lps[1]); ASSERT_TRUE(lps[1] > lps[2]); } static void test_chosen_logprob_matches_its_entry(void) { float logits[VOCAB]; reset(logits); logits[5] = 10.0f; logits[2] = 9.0f; uint32_t ids[PN_LOGPROBS_MAX_TOP]; float lps[PN_LOGPROBS_MAX_TOP]; float chosen = 0.0f; /* Chosen is the RUNNER-UP: its reported logprob must equal its top-list * entry, not the argmax's. */ pn_logprobs_extract(logits, VOCAB, 2, 2, &chosen, ids, lps); ASSERT_INT_EQ((int)ids[1], 2); ASSERT_FLT_NEAR(chosen, lps[1], 1e-6f); } static void test_logprobs_top_is_clamped(void) { float logits[VOCAB]; reset(logits); uint32_t ids[PN_LOGPROBS_MAX_TOP]; float lps[PN_LOGPROBS_MAX_TOP]; /* Asking for more than the cap must not overrun the caller's arrays. */ int n = pn_logprobs_extract(logits, VOCAB, 0, 10000, NULL, ids, lps); ASSERT_TRUE(n <= PN_LOGPROBS_MAX_TOP); ASSERT_INT_EQ(n, VOCAB); /* vocab is smaller than the cap here */ } static void test_logprobs_zero_top_still_reports_chosen(void) { float logits[VOCAB]; reset(logits); logits[4] = 5.0f; float chosen = 123.0f; int n = pn_logprobs_extract(logits, VOCAB, 4, 0, &chosen, NULL, NULL); ASSERT_INT_EQ(n, 0); ASSERT_TRUE(chosen < 0.0f); /* a real logprob was written */ ASSERT_TRUE(chosen > -1.0f); /* and token 4 dominates, so it is near 0 */ } static void test_logprobs_does_not_modify_the_row(void) { float logits[VOCAB]; for (int i = 0; i < VOCAB; i++) logits[i] = (float)i; float before[VOCAB]; memcpy(before, logits, sizeof(before)); uint32_t ids[PN_LOGPROBS_MAX_TOP]; float lps[PN_LOGPROBS_MAX_TOP]; pn_logprobs_extract(logits, VOCAB, 1, 4, NULL, ids, lps); for (int i = 0; i < VOCAB; i++) ASSERT_FLT_NEAR(logits[i], before[i], 0.0f); } int main(void) { printf("\n=== sampling modifiers (logit_bias / penalties / seed / logprobs) ===\n\n"); RUN_TEST(logit_bias_adds_to_the_named_tokens); RUN_TEST(logit_bias_ignores_out_of_range_ids); RUN_TEST(logit_bias_off_is_a_noop); RUN_TEST(frequency_penalty_scales_with_count); RUN_TEST(presence_penalty_is_charged_once); RUN_TEST(both_penalties_compose); RUN_TEST(penalties_off_is_a_noop); RUN_TEST(negative_penalty_boosts); RUN_TEST(same_seed_gives_the_same_stream); RUN_TEST(different_seeds_diverge_immediately); RUN_TEST(seed_zero_is_not_a_degenerate_state); RUN_TEST(rng_stays_in_unit_interval); RUN_TEST(logprobs_sum_to_one); RUN_TEST(logprobs_are_descending_and_top_is_argmax); RUN_TEST(chosen_logprob_matches_its_entry); RUN_TEST(logprobs_top_is_clamped); RUN_TEST(logprobs_zero_top_still_reports_chosen); RUN_TEST(logprobs_does_not_modify_the_row); TEST_REPORT(); return __ft_failed == 0 ? 0 : 1; }