DEV Community

Multigrid
Multigrid

Posted on • Originally published at multigrid.ai

Min-P Sampling Explained

Min-p keeps every token whose probability is at least a fixed fraction of the most likely token’s. That one sentence has a consequence most descriptions skip: the size of the surviving set is decided by the model, not by you.

The rule

Let p_max be the probability of the top-ranked token and p the setting. A token survives if its probability is at least p × p_max. Everything below is removed, and the remainder is renormalised and sampled from.

The threshold is relative, and that is the whole design. A fixed absolute cut-off — keep everything above 0.02, say — is too permissive where the model is certain and too strict where it is genuinely uncertain. Min-p asks a different question: not “is this token likely enough in absolute terms” but “is it a serious rival to the leader”.

What llama.cpp actually computes

The implementation is more elegant than the description, and worth seeing, because it explains a behaviour you would otherwise find surprising. In src/llama-sampler.cpp the min-p sampler never computes a probability at all. It takes the maximum logit and computes min_logit = max_logit + logf(p), then keeps every token whose logit is at least that.

// llama.cpp, src/llama-sampler.cpp
const float min_logit = max_logit + logf(ctx->p); // min logit for p_i >= p * p_max
Enter fullscreen mode Exit fullscreen mode

The step is exact, not an approximation. Softmax probabilities are exp(logit) / Z with the same normaliser Z for every token, so the ratio p_i / p_max is exp(logit_i - max_logit) and Z cancels. Requiring that ratio to be at least p is exactly requiring logit_i - max_logit ≥ log(p). With p = 0.05, log(0.05) = -3.0: keep every token within 3.0 logits of the leader, and nothing else.

That framing is the useful mental model. Min-p is a constant-width window below the top logit. It is one pass over the candidates with no sort required — llama.cpp has an unsorted fast path that does exactly that and only falls back to sorting if the filter would leave fewer candidates than min_keep demands.

How the cut moves with confidence

Work the same setting through two different distributions, with p = 0.05 throughout.

  • A confident step. The model is completing “the capital of France is” and puts 0.95 on one token. The threshold is 0.05 × 0.95 = 0.0475. Almost nothing else in the vocabulary clears 4.75%, so the surviving set is one or two tokens and sampling is effectively deterministic.
  • An open step. The model is choosing the next word of a sentence and the best token has probability 0.06. The threshold drops to 0.05 × 0.06 = 0.003, and every token above 0.3% survives — possibly dozens of them. Diversity is available exactly where the model says there is a real choice.

A fixed top-k cannot do this: k = 40 keeps 40 candidates in both cases, including 39 the model considered ridiculous in the first. Nor can a nucleus threshold expressed as cumulative mass, which is a different question about the distribution’s shape — see the sampling parameters page for how the truncation samplers stack. Min-p’s scale-invariance is the property being bought.

The behaviour that follows is the one min-p is usually reached for. A model asked to produce a fact has a sharply peaked distribution, so the window admits nothing and the answer is stable; the same model asked to choose an adjective has a flat distribution, so the window opens and the choice is genuinely varied. You get both from one setting, without a per-prompt decision, because the model’s own confidence is carrying the information. That is also why min-p is the truncation people pair with an unusually high temperature: the floor keeps the obviously wrong tokens out no matter how much the temperature flattens what remains.

It runs before temperature

llama.cpp’s default sampler chain, in common/common.h, is: penalties, DRY, top-n-sigma, top-k, typical-p, top-p, min-p, XTC, temperature. Temperature is last.

So min-p sees raw logits, and raising the temperature does not widen the set min-p kept. This surprises people who reason about temperature as a global “creativity” dial: with min-p active, temperature only redistributes probability inside a set that was already chosen at temperature 1. If you want temperature to feed min-p, you have to say so by reordering the chain with --samplers. Otherwise the honest description is that min-p caps how far off the leader the sampler may wander, and temperature decides how it moves within that cap.

Choosing a value

llama.cpp’s default is 0.05, with 0.0 disabling the sampler. The value is a likelihood ratio, so it reads naturally: 0.1 means “no token worse than a tenth of the leader”, which is a fairly tight window; 0.02 is loose. Because the effect compounds with any other truncation left enabled, the common recipe is to run min-p as the only truncation — set top-k to 0 and top-p to 1.0 — so that one parameter is doing one job and you can tell what changed.

One guard is worth knowing about before you push the value high. min_keep forces every truncation sampler to leave at least that many candidates standing, and llama.cpp threads it through min-p as well: the filter that would have cut below min_keep is refused and the sorted path stops at that count instead. It defaults to 0, so by default nothing protects you from a setting that keeps exactly one token at every step — which is greedy decoding reached by an expensive route. If you set p above about 0.3 and the output goes flat and repetitive, that is the mechanism, and it is not the repetition penalty’s job to fix it; see what the repeat penalty actually does.

The technique was introduced in “Turning Up the Heat: Min-p Sampling for Creative and Coherent LLM Outputs” by Nguyen and colleagues, arXiv:2407.01082, first posted July 2024 and presented at ICLR 2025. A later paper, arXiv:2506.13681 (June 2025), disputes that paper’s evaluation, including its handling of baseline scores. The mechanism above is not in dispute and is what the code does; the size of the quality claim over other samplers is, and a page that quoted a win rate from either paper as settled would be misleading you.

Defaults move. The 0.05 above is llama.cpp’s sampling default at the time of writing; other runtimes ship different ones, and a server in front of the model may override it per request.

Related

Top comments (0)