Skip to content
Merged
100 changes: 100 additions & 0 deletions common/sampling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "fit.h"
#include "log.h"
#include "reasoning-budget.h"
#include "speculative.h"

#include "ggml.h"

Expand All @@ -12,6 +13,7 @@
#include <climits>
#include <cmath>
#include <cstring>
#include <random>
#include <unordered_map>
#include <vector>

Expand Down Expand Up @@ -121,10 +123,14 @@ struct common_sampler {

llama_token_data_array cur_p;

uint32_t speculative_seed;
std::mt19937 speculative_rng;

void reset() {
prev.clear();

llama_sampler_reset(chain);
speculative_rng.seed(speculative_seed);
}

void set_logits(struct llama_context * ctx, int idx) {
Expand Down Expand Up @@ -424,6 +430,8 @@ struct common_sampler * common_sampler_init(
params.backend_sampling = false;
}

// Keep verifier randomness independent from both target and draft sampling.
const uint32_t speculative_seed = llama_sampler_get_seed(chain) ^ 0x9e3779b9U;
auto * result = new common_sampler {
/* .params = */ params,
/* .grmr = */ grmr,
Expand All @@ -432,6 +440,8 @@ struct common_sampler * common_sampler_init(
/* .prev = */ ring_buffer<llama_token>(std::max(32, params.n_prev)),
/* .cur = */ {},
/* .cur_p = */ {},
/* .speculative_seed = */ speculative_seed,
/* .speculative_rng = */ std::mt19937(speculative_seed),
};

return result;
Expand Down Expand Up @@ -515,6 +525,8 @@ struct common_sampler * common_sampler_clone(common_sampler * gsmpl) {
/* .prev = */ gsmpl->prev,
/* .cur = */ gsmpl->cur,
/* .cur_p = */ gsmpl->cur_p,
/* .speculative_seed = */ gsmpl->speculative_seed,
/* .speculative_rng = */ gsmpl->speculative_rng,
};
}

Expand All @@ -535,6 +547,8 @@ void common_sampler_copy(const common_sampler * src, common_sampler * dst) {
dst->cur = src->cur;
dst->cur_p = src->cur_p;
dst->cur_p.data = src->cur_p.data ? dst->cur.data() : nullptr; // re-point to dst's buffer
dst->speculative_seed = src->speculative_seed;
dst->speculative_rng = src->speculative_rng;
dst->t_total_us = src->t_total_us;
}

Expand Down Expand Up @@ -705,6 +719,79 @@ std::vector<llama_token> common_sampler_sample_and_accept_n(struct common_sample
return result;
}

std::vector<llama_token> common_sampler_sample_and_accept_n(
struct common_sampler * gsmpl,
struct llama_context * ctx,
const std::vector<int> & idxs,
const llama_tokens & draft,
const std::vector<common_speculative_token_dist> & dists,
bool grammar_first) {
GGML_ASSERT(idxs.size() == draft.size() + 1);
GGML_ASSERT(dists.size() == draft.size());
Comment thread
ngxson marked this conversation as resolved.
Outdated

std::vector<llama_token> result;
result.reserve(idxs.size());

std::uniform_real_distribution<float> uniform(0.0f, 1.0f);
size_t i = 0;
for (; i < draft.size(); ++i) {
// Residual sampling needs the target distribution after every constraint.
const llama_token fallback = common_sampler_sample(gsmpl, ctx, idxs[i], true);
const auto & q = dists[i];
GGML_ASSERT(q.ids.size() == q.probs.size());

std::unordered_map<llama_token, float> q_probs;
q_probs.reserve(q.ids.size());
for (size_t j = 0; j < q.ids.size(); ++j) {
q_probs[q.ids[j]] += q.probs[j];
}
const auto q_prob = [&](llama_token id) {
const auto it = q_probs.find(id);
return it == q_probs.end() ? 0.0f : it->second;
};

auto * p = common_sampler_get_candidates(gsmpl, false);
float p_draft = 0.0f;
const float q_draft = q_prob(draft[i]);
for (size_t j = 0; j < p->size; ++j) {
if (p->data[j].id == draft[i]) {
p_draft = p->data[j].p;
break;
}
}

if (q_draft > 0.0f && uniform(gsmpl->speculative_rng) * q_draft <= p_draft) {
common_sampler_accept(gsmpl, draft[i], true);
result.push_back(draft[i]);
continue;
}

std::vector<float> residual(p->size);
float residual_sum = 0.0f;
for (size_t j = 0; j < p->size; ++j) {
residual[j] = std::max(0.0f, p->data[j].p - q_prob(p->data[j].id));
residual_sum += residual[j];
}

llama_token id = fallback;
if (residual_sum > 0.0f) {
std::discrete_distribution<size_t> sample(residual.begin(), residual.end());
id = p->data[sample(gsmpl->speculative_rng)].id;
}
common_sampler_accept(gsmpl, id, true);
result.push_back(id);
break;
}

if (i == draft.size()) {
const llama_token id = common_sampler_sample(gsmpl, ctx, idxs[i], grammar_first);
common_sampler_accept(gsmpl, id, true);
result.push_back(id);
}

return result;
}

std::vector<llama_token> common_sampler_sample_and_accept_n(struct common_sampler * gsmpl, struct llama_context * ctx, const llama_tokens & draft, bool grammar_first) {
std::vector<int> idxs(draft.size() + 1);
for (size_t i = 0; i < idxs.size(); ++i) {
Expand All @@ -714,6 +801,19 @@ std::vector<llama_token> common_sampler_sample_and_accept_n(struct common_sample
return common_sampler_sample_and_accept_n(gsmpl, ctx, idxs, draft, grammar_first);
}

std::vector<llama_token> common_sampler_sample_and_accept_n(
struct common_sampler * gsmpl,
struct llama_context * ctx,
const llama_tokens & draft,
const std::vector<common_speculative_token_dist> & dists,
bool grammar_first) {
std::vector<int> idxs(draft.size() + 1);
for (size_t i = 0; i < idxs.size(); ++i) {
idxs[i] = i;
}
return common_sampler_sample_and_accept_n(gsmpl, ctx, idxs, draft, dists, grammar_first);
}

uint32_t common_sampler_get_seed(const struct common_sampler * gsmpl) {
return llama_sampler_get_seed(gsmpl->chain);
}
Expand Down
18 changes: 18 additions & 0 deletions common/sampling.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
//

struct common_sampler;
struct common_speculative_token_dist;

// llama_sampler API overloads

Expand Down Expand Up @@ -85,6 +86,23 @@ llama_token common_sampler_sample(struct common_sampler * gsmpl, struct llama_co
//
std::vector<llama_token> common_sampler_sample_and_accept_n(struct common_sampler * gsmpl, struct llama_context * ctx, const std::vector<int> & idxs, const llama_tokens & draft, bool grammar_first = false);

// maximal-coupling verification for stochastic speculative decoding
std::vector<llama_token> common_sampler_sample_and_accept_n(
struct common_sampler * gsmpl,
struct llama_context * ctx,
const std::vector<int> & idxs,
const llama_tokens & draft,
const std::vector<common_speculative_token_dist> & dists,
bool grammar_first = false);

// assume idxs == [ 0, 1, 2, ..., draft.size() ]
std::vector<llama_token> common_sampler_sample_and_accept_n(
struct common_sampler * gsmpl,
struct llama_context * ctx,
const llama_tokens & draft,
const std::vector<common_speculative_token_dist> & dists,
bool grammar_first = false);

// assume idxs == [ 0, 1, 2, ..., draft.size() ]
std::vector<llama_token> common_sampler_sample_and_accept_n(struct common_sampler * gsmpl, struct llama_context * ctx, const llama_tokens & draft, bool grammar_first = false);

Expand Down
Loading
Loading