voice_audio_multi/tests/community_models/fish_drama3/sampler_test.cpp
Fabio c36510c21e
Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
Initial import: audio.cpp with Orange Pi documentation
2026-11-04 19:05:22 +01:00

147 lines
8.1 KiB
C++

#include "../../../src/community_models/fish_drama3/generator.cpp"
#include <ggml-alloc.h>
#include <iostream>
namespace {
int32_t original_sample(const std::vector<float> & logits, float temperature, int top_k,
float top_p, std::mt19937 & random) {
std::vector<int32_t> order(logits.size());
std::iota(order.begin(), order.end(), 0);
const auto count = std::min(logits.size(), static_cast<size_t>(top_k));
std::partial_sort(order.begin(), order.begin() + count, order.end(), [&](int32_t a, int32_t b) {
return logits[a] == logits[b] ? a < b : logits[a] > logits[b];
});
order.resize(count);
if (temperature == 0 || top_k == 1) return order.front();
const double maximum = logits[order.front()] / temperature;
std::vector<double> probabilities(order.size());
double total = 0;
for (float logit : logits) total += std::exp(logit / temperature - maximum);
for (size_t rank = 0; rank < order.size(); ++rank)
probabilities[rank] = std::exp(logits[order[rank]] / temperature - maximum);
double cumulative = 0;
for (size_t rank = 0; rank < order.size(); ++rank) {
cumulative += probabilities[rank] / total;
if (rank > 0 && (cumulative > top_p || rank >= static_cast<size_t>(top_k))) probabilities[rank] = 0;
}
std::discrete_distribution<size_t> choice(probabilities.begin(), probabilities.end());
return order[choice(random)];
}
void test_device_top_p_boundary(engine::core::BackendType backend_type) {
engine::core::ExecutionContext execution({backend_type, 0, 1});
std::unique_ptr<ggml_context, decltype(&ggml_free)> context(ggml_init({1024 * 1024, nullptr, true}), ggml_free);
if (!context) throw std::runtime_error("sampler test context allocation failed");
auto * ctx = context.get();
auto * probs = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 4);
auto * top_p = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);
auto * top_k = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);
auto * uniform = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1);
ggml_set_input(probs);
ggml_set_input(top_p);
ggml_set_input(top_k);
ggml_set_input(uniform);
auto * cumulative = ggml_cumsum(ctx, probs);
auto * ranks = ggml_arange(ctx, 0, 4, 1);
auto * first = ggml_clamp(ctx, ggml_scale_bias(ctx, ranks, -1, 1), 0, 1);
auto * keep = ggml_scale_bias(ctx, ggml_clamp(ctx, ggml_sgn(ctx, ggml_sub(ctx, cumulative, top_p)), 0, 1), -1, 1);
keep = ggml_clamp(ctx, ggml_add(ctx, keep, first), 0, 1);
keep = ggml_mul(ctx, keep, ggml_clamp(ctx, ggml_add(ctx, ggml_scale(ctx, ranks, -1), top_k), 0, 1));
auto * cdf = ggml_cumsum(ctx, ggml_mul(ctx, probs, keep));
auto * total = ggml_view_1d(ctx, cdf, 1, 3 * sizeof(float));
auto * threshold = ggml_mul(ctx, total, uniform);
auto * selected = ggml_cast(
ctx, ggml_sum(ctx, ggml_clamp(ctx, ggml_sgn(ctx, ggml_add(ctx, ggml_scale(ctx, cdf, -1), threshold)), 0, 1)),
GGML_TYPE_I32);
ggml_set_output(keep);
ggml_set_output(selected);
auto * graph = ggml_new_graph(ctx);
ggml_build_forward_expand(graph, keep);
ggml_build_forward_expand(graph, selected);
std::unique_ptr<ggml_gallocr, decltype(&ggml_gallocr_free)> allocator(
ggml_gallocr_new(ggml_backend_get_default_buffer_type(execution.backend())), ggml_gallocr_free);
if (!ggml_gallocr_alloc_graph(allocator.get(), graph)) {
throw std::runtime_error("sampler test graph allocation failed");
}
const std::array<float, 4> probabilities{0.25F, 0.25F, 0.25F, 0.25F};
for (float cutoff : {0.125F, std::nextafter(0.5F, 0.0F), 0.5F, std::nextafter(0.5F, 1.0F), 1.0F}) {
for (float k : {1.0F, 2.0F, 4.0F}) {
for (float draw : {0.0F, 0.5F, 0.75F}) {
ggml_backend_tensor_set(probs, probabilities.data(), 0, sizeof(probabilities));
ggml_backend_tensor_set(top_p, &cutoff, 0, sizeof(cutoff));
ggml_backend_tensor_set(top_k, &k, 0, sizeof(k));
ggml_backend_tensor_set(uniform, &draw, 0, sizeof(draw));
if (ggml_backend_graph_compute(execution.backend(), graph) != GGML_STATUS_SUCCESS) {
throw std::runtime_error("sampler boundary graph failed");
}
std::array<float, 4> actual{};
ggml_backend_tensor_get(keep, actual.data(), 0, sizeof(actual));
std::array<float, 4> expected_cdf{};
float mass = 0;
for (size_t rank = 0; rank < probabilities.size(); ++rank) {
const float prefix = 0.25F * static_cast<float>(rank + 1);
const float expected = (rank == 0 || prefix <= cutoff) && rank < k ? 1.0F : 0.0F;
if (actual[rank] != expected) {
throw std::runtime_error("device top-p mask disagrees at rank " + std::to_string(rank) +
", top_p=" + std::to_string(cutoff) +
", mask=" + std::to_string(actual[rank]));
}
mass += expected * 0.25F;
expected_cdf[rank] = mass;
}
int32_t choice = -1;
ggml_backend_tensor_get(selected, &choice, 0, sizeof(choice));
const auto expected_choice =
std::lower_bound(expected_cdf.begin(), expected_cdf.end(), mass * draw) - expected_cdf.begin();
if (choice != expected_choice)
throw std::runtime_error("device sampler CDF boundary disagrees with host");
}
}
}
engine::core::release_backend_graph_resources(execution.backend(), graph, true);
}
}
int main(int argc, char ** argv) {
try {
engine::debug::configure_logging({true, std::nullopt});
const auto started = std::chrono::steady_clock::now();
const auto backend = argc > 2 && std::string(argv[1]) == "--backend"
? std::string(argv[2]) : std::string("cpu");
if (backend != "cpu" && backend != "cuda" && backend != "vulkan") {
throw std::runtime_error("sampler test backend must be cpu, cuda, or vulkan");
}
test_device_top_p_boundary(backend == "cuda" ? engine::core::BackendType::Cuda
: backend == "vulkan" ? engine::core::BackendType::Vulkan : engine::core::BackendType::Cpu);
std::cout << "device_top_p_boundary_matches=true backend=" << backend << '\n';
std::mt19937 source(1729);
std::normal_distribution<float> normal(0, 3);
size_t draws = 0;
for (size_t size : {size_t(1), size_t(17), size_t(65), size_t(257), size_t(4096), size_t(4097)}) {
for (bool tied : {false, true}) {
std::vector<float> logits(size);
for (auto & logit : logits) logit = tied ? float(source() % 7) : normal(source);
for (const float temperature : {0.0F, 0.01F, 0.7F, 1.0F, 1.5F}) {
for (const int top_k : {1, 30, 64, 128, int(size + 4)}) {
for (const float top_p : {0.01F, 0.9F, 0.95F, 1.0F}) {
std::mt19937 expected(1234), actual(1234);
engine::models::fish_drama3::SamplerScratch scratch;
for (int repeat = 0; repeat < 32; ++repeat) {
const auto a = original_sample(logits, temperature, top_k, top_p, expected);
const auto b = engine::models::fish_drama3::sample(logits, temperature, top_k, top_p, actual, scratch);
if (a != b || expected != actual) throw std::runtime_error("Drama 3 sampler changed token or RNG state");
++draws;
}
}
}
}
}
}
std::cout << "draws=" << draws << " token_and_rng_matches=true\n";
engine::debug::timing_log_scalar("session.wall_ms", engine::debug::elapsed_ms(started));
} catch (const std::exception & error) {
std::cerr << error.what() << '\n';
return 1;
}
}