LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 196 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/9
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningBayesian Inference from EvidenceStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/15
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageStatistical Learning and GeneralizationConformal Prediction and UncertaintyClustering and PCAProbabilistic Generative ModelsCore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingCausal Inference and CounterfactualsPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/25
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernancePrivacy-Preserving Machine LearningData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/17
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningTraining Run OperationsReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/31
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceAccelerator Architecture Field GuideCorrect GPU Profiling and BenchmarkingGPU Kernel Performance Engineering LabGPU Parallel Primitives LabGPU Compilers and Kernel LanguagesGEMM From Naive CUDA to Tensor CoresLow-Precision GPU ProgrammingGPU Runtime Execution LabAI-Generated Kernel EngineeringModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingGPU Failure OperationsDistributed Inference Data PlaneServing Benchmark EngineeringA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnMath & StatisticsHypothesis Tests, Intervals, and pass@k
📊EasyEvaluation & Benchmarks

Hypothesis Tests, Intervals, and pass@k

Compare a code-generation model with paired evidence, uncertainty for lift, and pass@k under a fixed sampling budget.

25 min read
Learning path
Step 18 of 196 in the full curriculum
Distributions and SamplingNeural Networks from Scratch

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

Model B passed one extra coding task. Is that a durable upgrade, or just a lucky roll of the dice?

A batch solve rate wobbles from run to run, and putting an interval around one model's score still doesn't settle whether it beats its predecessor. In this lesson, we test a coding-assistant upgrade using a suite of six platform-helper tasks: parsing timestamp windows, permission fallbacks, retry budgets, event deduplication, quota rounding, and cache invalidation. Both models face identical prompts and hidden tests that mark each candidate pass or fail. The numbers stay small enough to verify every calculation by hand.

The estimation lesson bounded uncertainty around a single rate; sampling and simulation showed how repeated draws scatter an estimate. Here the target is the difference between two systems. All outcomes below are illustrative fixtures rather than benchmarks of commercial checkpoints. The Python examples rely purely on the standard library.

Before inspecting the task rows, consider a simple question: if five tasks tie and one favors Model B, which rows tell you who actually won?

Six paired coding-helper tasks with Model A and Model B pass or fail. Five rows are ties. Only the permission row is a disagreement, a plus one for B. A count chart shows five ties, one B win, and zero A wins.
Five of the six paired tasks produce identical outcomes for both models. Those ties set the baseline pass rates, but only the permission task (+1 for B) provides directional evidence about which model is superior.

Start with paired pass or fail outcomes

Both models receive the exact same prompt and face the exact same hidden unit tests. That pairing anchors the entire benchmark design. If prompts, test harnesses, or decoding parameters drift between runs, you're no longer measuring a model upgrade; you're measuring environmental noise. Treat each task as a single paired observation rather than two disconnected scores.

The evaluation flow keeps that paired structure visible: one prompt fans out to two completions, which face identical unit tests before computing the task difference.

Diagram showing Same prompt, A completion, B completion, and Same hidden tests.
Same prompt, A completion, B completion, and Same hidden tests.

Now inspect the six task outcomes. Look for rows where the two result columns disagree.

TaskHidden-test requirementModel AModel B
1timestamp range is inclusivepasspass
2permission fallback preserves denialfailpass
3retry budget caps at configured limitpasspass
4event deduplication keeps latest versionfailfail
5quota rounding is deterministicpasspass
6cache invalidation skips expired keysfailfail

Convert pass to 1 and fail to 0. Model A's pass rate is 3 / 6 = 0.500, Model B's is 4 / 6 = 0.667, and the observed lift is 1 / 6 = 0.167, or 16.7 percentage points.

The next snippet prints those three quantities so you can check the arithmetic against the table.

paired-pass-rates.py
1model_a = [1, 0, 1, 0, 1, 0] 2model_b = [1, 1, 1, 0, 1, 0] 3 4rate_a = sum(model_a) / len(model_a) 5rate_b = sum(model_b) / len(model_b) 6lift = rate_b - rate_a 7 8print(f"Model A pass@1: {rate_a:.3f}") 9print(f"Model B pass@1: {rate_b:.3f}") 10print(f"observed lift: {lift:+.3f} ({lift * 100:+.1f} percentage points)")
Output
1Model A pass@1: 0.500 2Model B pass@1: 0.667 3observed lift: +0.167 (+16.7 percentage points)

Looking only at aggregate totals conceals the pairing. Subtract Model A's score from Model B's score on each row:

Result on one taskDifference B - ACount
both pass03
both fail02
B passes, A fails+11
A passes, B fails-10

Only disagreement tasks reveal which model performed better. Ties still shape both overall pass rates and the magnitude of the lift, but not the sign test's conditional win count. That distinction drives the first hypothesis test.

count-disagreements.py
1model_a = [1, 0, 1, 0, 1, 0] 2model_b = [1, 1, 1, 0, 1, 0] 3 4differences = [b - a for a, b in zip(model_a, model_b)] 5b_wins = differences.count(1) 6a_wins = differences.count(-1) 7ties = differences.count(0) 8 9print("paired differences:", differences) 10print(f"B wins={b_wins}, A wins={a_wins}, ties={ties}") 11print("directional evidence comes from disagreements:", b_wins + a_wins)
Output
1paired differences: [0, 1, 0, 0, 0, 0] 2B wins=1, A wins=0, ties=5 3directional evidence comes from disagreements: 1

A one-task lead exists in this sample. The central question is whether that lead is surprising if both models are equally capable. In other words, how often would a fair comparison produce at least this many wins for Model B?

How surprising is one B win?

Only one row disagreed, and Model B won it. If the two models were equally strong, that row could have fallen either way. The null hypothesis (H0H_0H0​) sets that baseline: on disagreement tasks, Model A and Model B are equally likely to win (p=0.5p = 0.5p=0.5). The directional alternative hypothesis (H1H_1H1​) states that Model B wins more frequently (p>0.5p > 0.5p>0.5).

Drop the ties and treat each remaining pair as a fair coin toss: Model B wins or Model A wins. That is the exact sign test on disagreements. Dixon and Mood presented this paired sign-test formulation in 1946; the test conditions strictly on the number of disagreements and ignores tied outcomes.[1]Reference 1The Statistical Sign Testhttps://doi.org/10.1080/01621459.1946.10501898

This calculation assumes independent task pairs drawn from the task distribution you want to evaluate. Twenty near-duplicate prompts aren't twenty independent pieces of evidence. For related tasks derived from the same repository or template, use a cluster-level analysis instead of treating each variant as an independent observation.

Our six-task benchmark has one disagreement, and Model B won it. A one-sided p-value for the planned claim "Model B is better" asks:

If both models were equally likely to win a disagreement, how often would random chance hand Model B at least this many wins?

Before running the code, predict what happens with d=1d = 1d=1 and b=1b = 1b=1: can a single win ever be rare when a fair coin has only two outcomes?

If ddd represents the count of disagreements and bbb represents the count of Model B wins, the one-sided right-tail probability is:

p=∑w=bd(dw)(12)dp = \sum_{w=b}^{d} \binom{d}{w} \left(\frac{1}{2}\right)^dp=w=b∑d​(wd​)(21​)d

The sum counts every outcome at least as favorable to Model B as the observed result. A p-value is this tail probability under the null hypothesis. It isn't the probability that the null hypothesis is true, and it isn't the probability that Model B is better.[2]Reference 2ASA Statement on Statistical Significance and P-Valueshttps://www.amstat.org/asa/files/pdfs/P-ValueStatement.pdf

With one disagreement, Model B wins it with probability 0.50.50.5. That result isn't surprising in the slightest.

Two fair-coin null distributions for B wins. With one disagreement the two outcomes each have probability 0.50, so the observed B win is not rare. With sixteen disagreements the mass peaks at eight B wins and the tail of thirteen or more has probability 0.011.
Under independent fair-coin disagreement signs, one B win out of one has a right-tail probability of 0.50. Thirteen or more B wins out of sixteen has probability about 0.011. Both probabilities describe outcomes under the null, not the chance that the null is true.

The next function evaluates that exact tail for the one-disagreement fixture and for a larger 13-to-3 outcome:

exact-directional-p-value.py
1from math import comb 2 3def b_wins_one_sided_p_value(b_wins: int, a_wins: int) -> float: 4 if b_wins < 0 or a_wins < 0: 5 raise ValueError("win counts must be nonnegative") 6 disagreements = b_wins + a_wins 7 if disagreements == 0: 8 return 1.0 9 tail_count = sum(comb(disagreements, wins) for wins in range(b_wins, disagreements + 1)) 10 return tail_count / (2 ** disagreements) 11 12print(f"one B win, zero A wins: p={b_wins_one_sided_p_value(1, 0):.3f}") 13print(f"thirteen B wins, three A wins: p={b_wins_one_sided_p_value(13, 3):.4f}")
Output
1one B win, zero A wins: p=0.500 2thirteen B wins, three A wins: p=0.0106

Thirteen wins to three is a completely different story. If you had declared "Model B is better" before seeing the outcomes, that one-sided tail probability is 0.01060.01060.0106. If you had planned to detect a difference in either direction, the symmetric two-sided binomial test yields about 0.02130.02130.0213 here. Never pick the favorable direction after inspecting who won.[3]Reference 3scipy.stats.binomtesthttps://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.binomtest.html

Model B passes four tasks and Model A passes three, but there is only one disagreement. What is the safe conclusion?

Answer

Model B scored higher in this small sample. The single disagreement gives weak evidence for a durable improvement, so you should collect more paired tasks before claiming the model is better.

A p-value answers whether a win is surprising under the null. It doesn't tell you how large the lift is, or whether the test had enough power to detect a real gain.

Type I errors, Type II errors, and statistical power

Every model-review decision is a choice made under uncertainty. In statistical evaluation, two distinct mistakes can occur:

Reality \ DecisionRetain H0H_0H0​ (Keep Model A)Reject H0H_0H0​ (Ship Model B)
H0H_0H0​ is true (B is no better)Correct retention (Probability: 1−α1 - \alpha1−α)Type I error (α\alphaα, False Positive): Ship phantom gain
H1H_1H1​ is true (B is genuinely better)Type II error (β\betaβ, False Negative): Shelve real improvementStatistical Power (1−β1 - \beta1−β): Successfully detect lift

A Type I error (α\alphaα) is a false positive: rejecting the null hypothesis when Model B is actually no better (or worse) than Model A. Shipping a false win burns GPU memory, invalidates inference caches, complicates rollbacks, and risks silent user regressions. Setting a significance threshold such as α=0.05\alpha = 0.05α=0.05 caps the probability of making a Type I error at 5% under the baseline assumption that the null hypothesis is true.

A Type II error (β\betaβ) is a false negative: failing to reject the null hypothesis when Model B is genuinely superior. An engineer trains a refined checkpoint that produces a real 3 percentage point lift, but the evaluation suite contains too few tasks to confirm it, so the model gets shelved.

Statistical power (1−β1 - \beta1−β) measures the probability of detecting a real improvement of a specific effect size δ\deltaδ. Power depends directly on four interacting variables:

  1. Effect size (δ\deltaδ): Large leaps in model capability are easy to detect; subtle 1% to 2% gains require extensive data.
  2. Sample size (NNN): Larger task suites provide more chances to observe disagreements and narrow the sampling error.
  3. Variance (σ2\sigma^2σ2): Noisy prompts obscure the underlying difference.
  4. Significance threshold (α\alphaα): Stricter false-positive gates (like α=0.01\alpha = 0.01α=0.01) demand stronger evidence, which lowers power unless sample size increases.

In our six-task benchmark with a single disagreement, statistical power against even a massive 20-point lift is practically zero. Because a single fair coin toss has probability 0.500.500.50, it's mathematically impossible for any one-disagreement result to produce p≤0.05p \le 0.05p≤0.05. You could never reject the null on one disagreement, no matter how much better Model B truly is.

Even with 16 disagreements, power against subtle improvements remains modest. When an evaluation yields p>0.05p > 0.05p>0.05, that doesn't mean the two models are identical; it often just means the evaluation lacked the statistical power to resolve the difference. Never mistake absence of evidence for evidence of absence.

Looking for a winner changes the test

Suppose the 13-to-3 result was the best of twenty prompt variants tried against Model A. Reporting only its p=0.0106p = 0.0106p=0.0106 leaves out nineteen opportunities to find a small p-value by pure chance.

A significance level, such as α=0.05\alpha = 0.05α=0.05, limits the false-rejection probability for a single, preplanned test under its null. It doesn't guarantee a five-percent chance of error when searching through a catalog of experiments. If twenty independent tests each falsely reject with probability 0.050.050.05, the probability of observing at least one false rejection across the family is:

1−(1−0.05)20≈64%1 - (1 - 0.05)^{20} \approx 64\%1−(1−0.05)20≈64%

One standard safeguard is the Bonferroni correction: for twenty planned comparisons, test each individual comparison at α/M=0.05/20=0.0025\alpha / M = 0.05 / 20 = 0.0025α/M=0.05/20=0.0025. This adjustment bounds the family-wise error rate even when tests are dependent, provided each p-value is valid. Our 0.01060.01060.0106 result doesn't clear that bar. Bonferroni can be conservative, but the core discipline is to account for all comparisons searched, not just the winner.[4]Reference 4NIST/SEMATECH e-Handbook: Bonferroni's Methodhttps://www.itl.nist.gov/div898/handbook/prc/section4/prc473.htm

Repeatedly inspecting the same benchmark and halting execution the moment p<0.05p < 0.05p<0.05 is another form of hidden search. Fix the sample size and analysis protocol in advance, or use sequential testing methods designed for continuous monitoring. If an evaluation suite guided prompt or hyperparameter selection, validate the chosen checkpoint on an untouched holdout benchmark. A low p-value isn't a replacement for clean data splits.[2]Reference 2ASA Statement on Statistical Significance and P-Valueshttps://www.amstat.org/asa/files/pdfs/P-ValueStatement.pdf

Why paired evaluations crush variance

Why do rigorous LLM benchmarks insist on paired evaluations rather than testing Model A on one set of prompts and Model B on another? The mathematical explanation rests on prompt difficulty covariance.

Let Xi∈{0,1}X_i \in \{0, 1\}Xi​∈{0,1} represent Model A's pass/fail outcome on task iii, and let Yi∈{0,1}Y_i \in \{0, 1\}Yi​∈{0,1} represent Model B's outcome.

If you test Model A on NNN independent prompts and Model B on a separate, unpaired batch of NNN prompts, the variance of the estimated difference between the two sample means is the sum of their individual variances:

Var(Yˉ−Xˉ)=σX2+σY2N\text{Var}(\bar{Y} - \bar{X}) = \frac{\sigma_X^2 + \sigma_Y^2}{N}Var(Yˉ−Xˉ)=NσX2​+σY2​​

In code generation, prompt difficulty varies across an enormous range. Writing an inclusive timestamp filter is straightforward for modern models (roughly a 95% solve rate), while resolving concurrency race conditions or subtle cache invalidations is brutally difficult (often below a 10% solve rate).

When both models attempt the exact same prompt, they share that prompt's difficulty. If task iii is trivial, both models pass; if task iii is exceptionally hard, both models fail. Because prompt difficulty affects both models simultaneously, XiX_iXi​ and YiY_iYi​ exhibit strong positive correlation (Cov(X,Y)>0\text{Cov}(X, Y) > 0Cov(X,Y)>0, typically with correlation ρ∈[0.70,0.90]\rho \in [0.70, 0.90]ρ∈[0.70,0.90]).

In a paired design, the estimator is the sample mean of the row-by-row differences Di=Yi−XiD_i = Y_i - X_iDi​=Yi​−Xi​:

Var(Dˉ)=Var(Yi−Xi)N=σX2+σY2−2Cov(X,Y)N\text{Var}(\bar{D}) = \frac{\text{Var}(Y_i - X_i)}{N} = \frac{\sigma_X^2 + \sigma_Y^2 - 2\text{Cov}(X, Y)}{N}Var(Dˉ)=NVar(Yi​−Xi​)​=NσX2​+σY2​−2Cov(X,Y)​

The covariance term −2Cov(X,Y)-2\text{Cov}(X, Y)−2Cov(X,Y) directly subtracts out the between-prompt variance.

Consider concrete numbers for a benchmark of N=40N = 40N=40 tasks. Suppose each model has individual pass variance σX2=σY2=0.25\sigma_X^2 = \sigma_Y^2 = 0.25σX2​=σY2​=0.25 (corresponding to a 50% baseline pass rate), and the prompt difficulty correlation is ρ=0.80\rho = 0.80ρ=0.80, giving Cov(X,Y)=ρσXσY=0.80×0.5×0.5=0.20\text{Cov}(X, Y) = \rho \sigma_X \sigma_Y = 0.80 \times 0.5 \times 0.5 = 0.20Cov(X,Y)=ρσX​σY​=0.80×0.5×0.5=0.20.

In an unpaired test, the variance is:

Var(Yˉ−Xˉ)=0.25+0.2540=0.5040=0.0125\text{Var}(\bar{Y} - \bar{X}) = \frac{0.25 + 0.25}{40} = \frac{0.50}{40} = 0.0125Var(Yˉ−Xˉ)=400.25+0.25​=400.50​=0.0125

The standard error is 0.0125≈0.1118\sqrt{0.0125} \approx 0.11180.0125​≈0.1118 (11.2 percentage points). An approximate 95% margin of error (±1.96×SE\pm 1.96 \times \text{SE}±1.96×SE) spans ±21.9\pm 21.9±21.9 percentage points. A genuine +7.5 point lift gets lost inside a wide [−14.4,+29.4][-14.4, +29.4][−14.4,+29.4] interval.

In a paired test, the covariance term activates:

Var(Dˉ)=0.25+0.25−2(0.20)40=0.50−0.4040=0.1040=0.0025\text{Var}(\bar{D}) = \frac{0.25 + 0.25 - 2(0.20)}{40} = \frac{0.50 - 0.40}{40} = \frac{0.10}{40} = 0.0025Var(Dˉ)=400.25+0.25−2(0.20)​=400.50−0.40​=400.10​=0.0025

The variance drops by a factor of 5. The standard error shrinks to 0.0025=0.050\sqrt{0.0025} = 0.0500.0025​=0.050 (5.0 percentage points), narrowing the 95% error margin to ±9.8\pm 9.8±9.8 percentage points ([−2.3,+17.3][-2.3, +17.3][−2.3,+17.3]).

Comparison of unpaired and paired evaluation variance. Unpaired evaluation yields a variance of 0.0125 and a wide 95% error margin of plus or minus 21.9 percentage points. Paired evaluation cancels prompt-difficulty covariance, yielding a 5x smaller variance of 0.0025 and a narrow margin of plus or minus 9.8 points.
Prompt difficulty covariance acts as a natural noise filter. When both models face identical prompts, the positive covariance term cancels between-prompt difficulty noise, reducing variance by 5x and tightening error margins.

Pairing filters out prompt-to-prompt noise. It gives 40 paired tasks the statistical resolving power of 200 unpaired tasks.

An interval for B minus A

A confidence interval should target the quantity behind the decision. If the question is "How much better is Model B than Model A on this task suite?", build an interval around the paired lift B−AB - AB−A.

Comparing two separate pass-rate intervals can hide the pairing. The evaluation report needs a single interval for the row-by-row difference.

The 95% label describes the intended long-run coverage of the interval procedure over repeated experiments. It doesn't mean this specific realized interval has a 95% probability of containing the true parameter. Classical textbook formulas (such as the Wald interval p^±1.96p^(1−p^)/N\hat{p} \pm 1.96\sqrt{\hat{p}(1-\hat{p})/N}p^​±1.96p^​(1−p^​)/N​) fail on small or discrete benchmarks: they assume symmetric normal sampling distributions, can generate impossible bounds outside [0,1][0, 1][0,1], and deliver poor coverage when success rates approach the boundaries.[5]Reference 5NIST/SEMATECH e-Handbook: What Are Confidence Intervals?https://www.itl.nist.gov/div898/handbook/prc/section1/prc14.htm

In the estimation chapter, bootstrap resampling placed an interval around a single metric by drawing observations with replacement.[6]Reference 6Bootstrap Methods: Another Look at the Jackknife.https://projecteuclid.org/journals/annals-of-statistics/volume-7/issue-1/Bootstrap-Methods--Another-Look-at-the-Jackknife/10.1214/aos/1176344552.full Here the unit of resampling is the paired task row: keep Model A's outcome beside Model B's outcome, then recompute the mean difference. The percentile bootstrap interval takes the 2.5th and 97.5th percentiles of those resampled lifts as its bounds. This task bootstrap reflects sampling variation across a broader population of comparable tasks.[7]Reference 7scipy.stats.bootstraphttps://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.bootstrap.html

Prediction check: Six tasks help develop intuition but are too few for a stable interval. Consider a slightly larger fixture with 40 paired tasks. Before running the resampler, predict whether a +7.5-point lift from only 13 disagreements will remain strictly positive:

Paired outcomeTasks
both pass17
both fail10
B passes, A fails8
A passes, B fails5

The observed lift is (8−5)/40=0.075(8 - 5) / 40 = 0.075(8−5)/40=0.075, or +7.5+7.5+7.5 percentage points. Bootstrap the paired differences to inspect how unstable that lift remains. The next cell resamples complete paired differences and reports the percentile interval.

paired-bootstrap-interval.py
1from random import Random 2 3differences = [0] * 27 + [1] * 8 + [-1] * 5 4rng = Random(7) 5n = len(differences) 6bootstrap_lifts = [ 7 sum(rng.choice(differences) for _ in range(n)) / n 8 for _ in range(20_000) 9] 10 11def quantile(values: list[float], q: float) -> float: 12 ordered = sorted(values) 13 pos = (len(ordered) - 1) * q 14 lo = int(pos) 15 hi = min(lo + 1, len(ordered) - 1) 16 frac = pos - lo 17 return ordered[lo] * (1 - frac) + ordered[hi] * frac 18 19low = quantile(bootstrap_lifts, 0.025) 20high = quantile(bootstrap_lifts, 0.975) 21observed = sum(differences) / n 22at_or_below_zero = sum(lift <= 0 for lift in bootstrap_lifts) / len(bootstrap_lifts) 23 24print(f"observed paired lift: {observed * 100:+.1f} percentage points") 25print(f"approximate 95% bootstrap interval: {low * 100:+.1f} to {high * 100:+.1f} points") 26print("interval includes zero:", low <= 0 <= high) 27print(f"share of resamples with lift <= 0: {at_or_below_zero:.0%}")
Output
1observed paired lift: +7.5 percentage points 2approximate 95% bootstrap interval: -10.0 to +25.0 points 3interval includes zero: True 4share of resamples with lift <= 0: 24%

Resampling the differences array is equivalent to resampling complete rows for this binary example because each row contributes one difference. With richer multi-metric evals, keep the full row together so the analysis preserves the pairing. The task row, not an isolated model score, remains the fundamental unit of resampling.

The printed 24% is a fraction of bootstrap resamples, not a p-value and not a Bayesian posterior probability that Model B is worse. These resamples center around the observed sample lift; they weren't generated under the zero-lift null hypothesis.

Approximate paired bootstrap interval for Model B minus Model A from minus 10 to plus 25 percentage points, with observed lift plus 7.5. Of 20,000 resamples, 16.8 percent have negative lift, 7.7 percent zero lift, and 75.5 percent positive lift. These are resample frequencies, not probabilities about the true lift.
The 2.5th and 97.5th percentiles give an approximate interval from −10 to +25 points. The bars describe the resampling distribution, not probabilities that the true lift is negative, zero, or positive.

Bootstrap intervals are approximate, especially with small or discrete samples. For instance, resampling six observed ties produces only zero lifts and a [0, 0] interval, even though six ties don't prove the models are identical on future tasks. Running more bootstrap iterations can't conjure missing evidence out of thin air. For our 40-task fixture, the actionable conclusion is: "Model B gained 7.5 points in this paired sample, but the approximate interval spans losses as well as gains."

Set a minimum useful lift threshold before examining results. If deploying Model B requires at least two percentage points of improvement to justify higher hosting costs, an interval of [+1, +14] points excludes zero but doesn't establish the required threshold. The next function formalizes those decisions:

language-from-lift-interval.py
1def comparison_claim( 2 observed_lift: float, 3 interval: tuple[float, float], 4 minimum_useful_lift: float = 0.02, 5) -> str: 6 low, high = interval 7 if low > high or minimum_useful_lift < 0: 8 raise ValueError("require ordered endpoints and a nonnegative useful lift") 9 if low > minimum_useful_lift: 10 return f"clears useful-lift threshold: estimated lift {observed_lift:+.3f}" 11 if low > 0: 12 return f"positive lift, useful size not established: estimate {observed_lift:+.3f}" 13 if high < 0: 14 return f"evidence of regression: estimated lift {observed_lift:+.3f}" 15 return f"inconclusive: estimated lift {observed_lift:+.3f}, interval includes zero" 16 17print(comparison_claim(0.075, (-0.100, 0.250))) 18print(comparison_claim(0.075, (0.010, 0.140))) 19print(comparison_claim(0.075, (0.030, 0.120)))
Output
1inconclusive: estimated lift +0.075, interval includes zero 2positive lift, useful size not established: estimate +0.075 3clears useful-lift threshold: estimated lift +0.075

So far each task evaluated a single completion from each model. Coding evaluations often permit several candidate attempts. That measures a different capability, and it demands its own evaluation protocol.

pass@k measures attempts

A coding assistant can generate multiple candidate functions, allowing an automated test harness to verify whether at least one candidate passes hidden tests. That is the capability measured by pass@k in functional code-generation benchmarks like HumanEval.[8]Reference 8Evaluating Large Language Models Trained on Code (HumanEval).https://arxiv.org/abs/2107.03374 The metric fixes a budget of kkk candidate attempts per task. An alternative completion may solve a task that the model's first attempt missed.

HumanEval comprises 164 handwritten Python coding problems with an average of 7.7 unit tests each. In that paper, the authors generated n=200n = 200n=200 samples per task (n≥kn \ge kn≥k) and reported kkk up to 100100100. Those parameters reflect their specific experimental design, not a rigid requirement. The estimator below works for any predeclared n≥kn \ge kn≥k that you run.

Consider this illustrative sample of three platform-helper tasks with three completions each. Follow the retry-state row: only the third candidate passes.

TaskAttempt 1Attempt 2Attempt 3first_attempt_hitany_of_3_hit
parse timestamp windowpassfailfail11
retry-state fallbackfailfailpass01
quota split roundingfailfailfail00

first_attempt_hit records whether Attempt 1 passed. any_of_3_hit records whether any of the three completions passed. Extra attempts discover additional solutions. These realized trials illustrate the underlying concept; a HumanEval-style harness evaluates all sampled candidates to compute an unbiased estimate.

Count each task once in both perspectives:

pass-at-k-from-attempt-table.py
1attempts = [ 2 [1, 0, 0], 3 [0, 0, 1], 4 [0, 0, 0], 5] 6 7first_attempt_hit = sum(row[0] for row in attempts) / len(attempts) 8any_of_3_hit = sum(any(row[:3]) for row in attempts) / len(attempts) 9 10print(f"first_attempt_hit: {first_attempt_hit:.3f}") 11print(f"any_of_3_hit: {any_of_3_hit:.3f}") 12print("extra solved tasks from extra attempts:", int((any_of_3_hit - first_attempt_hit) * len(attempts)))
Output
1first_attempt_hit: 0.333 2any_of_3_hit: 0.667 3extra solved tasks from extra attempts: 1

The ordered table answered a product question about a known first completion. For reporting standard HumanEval metrics, treat the nnn candidates as an unordered pool and evaluate the combinatorial estimator on the count of correct completions. For the same three tasks with n=3n = 3n=3 samples each, the correct counts are c=[1,1,0]c = [1, 1, 0]c=[1,1,0]. The unbiased pass@1 score is the mean of c/nc/nc/n:

13(13+13+03)≈0.222\frac{1}{3}\left(\frac{1}{3} + \frac{1}{3} + \frac{0}{3}\right) \approx 0.22231​(31​+31​+30​)≈0.222

When k=1k = 1k=1, the combinatorial formula simplifies to c/nc/nc/n, regardless of which completion was generated first. When n=k=3n = k = 3n=k=3, the combinatorial estimator equals the any_of_3_hit column. These aren't conflicting definitions: one is a single realized draw, while the estimator computes the expected value over all possible subsets drawn from the generated pool.

Keep two budgets distinct: kkk specifies the operational attempt budget being evaluated; nnn is the number of samples drawn to estimate that metric. You can estimate pass@1 using 200 samples without giving the end product 200 attempts.

When a task has n=10 candidates and c=2 correct, what is HumanEval-style pass@1?

Answer

0.2. For k=1 the combinatorial estimator is c/n. It doesn't depend on which of the ten samples came first in generation order.

Publish kkk, the sample pool size nnn, the decoding parameters, and the test suites used to verify functional correctness. A higher score under a larger candidate budget doesn't imply stronger single-completion performance. Chen and colleagues also observed that higher sampling temperature often benefits larger kkk, because more diverse candidates increase the chance that at least one completion solves the problem.[8]Reference 8Evaluating Large Language Models Trained on Code (HumanEval).https://arxiv.org/abs/2107.03374

Derive the HumanEval estimator by hand

Suppose the assistant generates n=10n = 10n=10 candidate implementations for a function, and hidden tests accept c=2c = 2c=2 of them. You want to compute the expected pass@5 score if you pick five candidates from that pool. Before calculating, predict which event is easier to count: at least one candidate passing, or all five candidates failing?

Counting successful subsets directly is tedious. Count the failure case instead. The event "at least one candidate passes" is the complement of "all five candidates fail":

  1. There are 10−2=810 - 2 = 810−2=8 failing candidates.
  2. There are (105)=252\binom{10}{5} = 252(510​)=252 total ways to pick five candidates from the pool.
  3. There are (85)=56\binom{8}{5} = 56(58​)=56 ways to pick five candidates that all fail.
  4. The probability that at least one candidate passes is 1−56/252=0.7781 - 56 / 252 = 0.7781−56/252=0.778.

In general notation:

pass@k=1−(n−ck)(nk)\text{pass@k} = 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}}pass@k=1−(kn​)(kn−c​)​

The target parameter is generative: the probability that at least one of kkk independent model completions succeeds, written 1−(1−p)k1 - (1-p)^k1−(1−p)k for an unknown per-token solve probability ppp. The combinatorial formula is an unbiased minimum-variance estimator (a U-statistic) of that generative quantity. It calculates the exact fraction of kkk-sized subsets of the nnn completions that contain at least one passing solution. The HumanEval protocol samples n≥kn \ge kn≥k and reports this combinatorial estimator rather than a naive plug-in.[8]Reference 8Evaluating Large Language Models Trained on Code (HumanEval).https://arxiv.org/abs/2107.03374

Here i.i.d. signifies independent draws from the same decoding distribution for this task. Fix nnn in advance and don't discard duplicate completions: identical outputs still carry probability mass. Halting early after the first passing completion, deduplicating outputs, or feeding test errors back into subsequent attempts alters the experimental distribution. A multi-turn agent that inspects error traces requires an evaluation of that complete iterative harness, not this single-turn formula.

Hidden tests serve as an evaluation oracle: pass@k credits a candidate pool whenever it contains a passing solution. It doesn't assess whether an unguided production system, without access to hidden unit tests, can pick out that passing candidate. Evaluate your reranker or selection model separately if the product must return a single answer.

The next snippet verifies the hand calculation with comb:

pass-at-k-hand-calculation.py
1from math import comb 2 3n = 10 4c = 2 5k = 5 6all_groups = comb(n, k) 7all_failing_groups = comb(n - c, k) 8score = 1 - all_failing_groups / all_groups 9 10print("all groups:", all_groups) 11print("all-failing groups:", all_failing_groups) 12print(f"pass@5: {score:.3f}")
Output
1all groups: 252 2all-failing groups: 56 3pass@5: 0.778

How the naive plug-in underestimates pass@k

A tempting shortcut calculates the empirical success rate and substitutes it into the generative formula:

pass@knaive=1−(1−p^)k\text{pass@k}_{\text{naive}} = 1 - (1 - \hat{p})^kpass@knaive​=1−(1−p^​)k

where p^=c/n\hat{p} = c / np^​=c/n is the observed single-sample pass rate. Chen and colleagues demonstrated that 1−(1−p^)k1 - (1 - \hat{p})^k1−(1−p^​)k is a biased underestimate of the generative pass@k probability 1−(1−p)k1 - (1 - p)^k1−(1−p)k.[8]Reference 8Evaluating Large Language Models Trained on Code (HumanEval).https://arxiv.org/abs/2107.03374

Two distinct mathematical principles explain this shortfall:

  1. Jensen's inequality on concave functions: Consider the function g(p)=1−(1−p)kg(p) = 1 - (1 - p)^kg(p)=1−(1−p)k. For k>1k > 1k>1 and p∈(0,1)p \in (0, 1)p∈(0,1), the second derivative with respect to ppp is strictly negative: g′′(p)=−k(k−1)(1−p)k−2<0g''(p) = -k(k - 1)(1 - p)^{k - 2} < 0g′′(p)=−k(k−1)(1−p)k−2<0 Because g(p)g(p)g(p) is strictly concave, Jensen's inequality guarantees that the expected value of the plug-in estimator is strictly smaller than the true function evaluated at the mean: E[1−(1−p^)k]<1−(1−E[p^])k=1−(1−p)k\mathbb{E}\left[1 - (1 - \hat{p})^k\right] < 1 - \left(1 - \mathbb{E}[\hat{p}]\right)^k = 1 - (1 - p)^kE[1−(1−p^​)k]<1−(1−E[p^​])k=1−(1−p)k Plugging a noisy empirical proportion into a concave curve inevitably pushes the estimate downward.
  2. Sampling with replacement versus without replacement: The plug-in expression (1−c/n)k(1 - c/n)^k(1−c/n)k represents drawing kkk samples with replacement from the finite pool of nnn completions. Each draw assumes an unchanged failure rate of (n−c)/n(n - c) / n(n−c)/n. But in an actual without-replacement draw, pulling a failing completion removes that failure from the denominator. The proportion of successes among the remaining candidates rises from c/nc/nc/n to c/(n−1)c / (n - 1)c/(n−1). Drawing without replacement depletes the failure pool, making a success more probable on subsequent picks.

For 0<c<n0 < c < n0<c<n and k>1k > 1k>1, the naive plug-in is strictly smaller than the unbiased combinatorial estimate on the same (n,c,k)(n, c, k)(n,c,k).

Comparison of the unbiased combinatorial pass@5 estimator and the naive plug-in estimator for n=10 samples with c=2 correct. The unbiased formula evaluates all 252 subsets without replacement, yielding 0.778. The naive plug-in assumes sampling with replacement and underestimates at 0.672, creating a 10.6 percentage point downward bias.
The unbiased combinatorial estimator evaluates all 252 subsets drawn without replacement from the 10-candidate pool, yielding 0.778. The naive plug-in assumes sampling with replacement and underestimates at 0.672 due to Jensen's inequality.

For n=10n = 10n=10, c=2c = 2c=2, and k=5k = 5k=5, compare both calculations directly:

naive-vs-unbiased-passk.py
1p_hat = c / n 2naive_score = 1.0 - (1.0 - p_hat) ** k 3unbiased_score = score # From previous cell: 1 - comb(8, 5) / comb(10, 5) 4 5shortfall = unbiased_score - naive_score 6print(f"Plug-in pass@5 1-(1-c/n)^k: {naive_score:.4f}") 7print(f"Unbiased combinatorial pass@5: {unbiased_score:.4f}") 8print(f"Plug-in shortfall vs unbiased estimate: {shortfall:.4f}") 9 10assert abs(naive_score - 0.67232) < 1e-5
Output
1Plug-in pass@5 1-(1-c/n)^k: 0.6723 2Unbiased combinatorial pass@5: 0.7778 3Plug-in shortfall vs unbiased estimate: 0.1055

Two boundary checks confirm the logic:

  • When c=0c = 0c=0, no selected group can pass.
  • When fewer than kkk failures exist (n−c<kn - c < kn−c<k), every kkk-sized group contains at least one passing candidate.

For larger nnn, assembling gigantic combinations can trigger floating-point overflow. The HumanEval paper provides an equivalent product formulation that stays numerically well-behaved:[8]Reference 8Evaluating Large Language Models Trained on Code (HumanEval).https://arxiv.org/abs/2107.03374

(n−ck)(nk)=∏i=n−c+1n(1−ki)\frac{\binom{n-c}{k}}{\binom{n}{k}} = \prod_{i=n-c+1}^{n} \left(1 - \frac{k}{i}\right)(kn​)(kn−c​)​=i=n−c+1∏n​(1−ik​)

It evaluates the failure probability factor by factor, avoiding factorials of large numbers.

stable-pass-at-k.py
1from math import prod 2 3def pass_at_k(n: int, c: int, k: int) -> float: 4 if n <= 0 or not 0 <= c <= n or not 1 <= k <= n: 5 raise ValueError("require n > 0, 0 <= c <= n, and 1 <= k <= n") 6 if n - c < k: 7 return 1.0 8 failure_probability = prod(1.0 - k / i for i in range(n - c + 1, n + 1)) 9 return 1.0 - failure_probability 10 11print(f"n=10, c=2, k=1: {pass_at_k(10, 2, 1):.3f}") 12print(f"n=10, c=2, k=5: {pass_at_k(10, 2, 5):.3f}") 13print(f"no passing candidates: {pass_at_k(10, 0, 5):.3f}") 14print(f"not enough failures: {pass_at_k(10, 8, 5):.3f}") 15 16try: 17 pass_at_k(10, 11, 5) 18except ValueError as error: 19 print(error)
Output
1n=10, c=2, k=1: 0.200 2n=10, c=2, k=5: 0.778 3no passing candidates: 0.000 4not enough failures: 1.000 5require n > 0, 0 <= c <= n, and 1 <= k <= n

Average pass@k across tasks

A benchmark score macro-averages task-level results. A complex permission-verification function counts as one task; it shouldn't be drowned out by hundreds of completions from an easy string helper. The estimator runs per task before the benchmark computes its overall mean.

Suppose four tasks each produce n=10n = 10n=10 candidates, with the following counts of correct completions:

text
1[0, 1, 2, 4]

Compute each task's estimate first, then average those four estimates. A task with zero passing completions contributes zero across every value of kkk.

macro-average-pass-at-k.py
1correct_counts = [0, 1, 2, 4] 2for k in (1, 3, 5): 3 task_scores = [pass_at_k(10, correct, k) for correct in correct_counts] 4 mean_score = sum(task_scores) / len(task_scores) 5 print(f"pass@{k}: {mean_score:.3f} per-task={[round(score, 3) for score in task_scores]}")
Output
1pass@1: 0.175 per-task=[0.0, 0.1, 0.2, 0.4] 2pass@3: 0.417 per-task=[0.0, 0.3, 0.533, 0.833] 3pass@5: 0.563 per-task=[0.0, 0.5, 0.778, 0.976]

Notice that pass@5 climbs to 0.5630.5630.563 while pass@1 sits at 0.1750.1750.175. That extra search capacity helps if your production product can verify multiple candidates. It doesn't indicate that single-completion quality improved. The metric grew because the attempt budget expanded, not because the underlying tasks became easier.

Changed protocols create fake wins

To compare models fairly under a fixed sampling policy, keep the task suite, unit tests, candidate budget kkk, and decoding rules matched. Record token cutoffs, execution timeouts, and candidate selection rules. A timeout shouldn't quietly vanish from the denominator.

Record nnn as well, but don't confuse it with kkk. Two independently sampled pools with different predeclared n≥kn \ge kn≥k estimate the same underlying pass@k; their precision differs. Matching nnn is a convenient balanced setup, not a mathematical necessity for a valid fixed-kkk benchmark. Don't omit challenging tasks simply because they generated fewer completions than requested: finish the protocol or report that the score can't be computed.[9]Reference 9HumanEval: Functional Correctness Evaluation and pass@k Estimatorhttps://github.com/openai/human-eval/blob/master/human_eval/evaluation.py

A deterministic decoder illustrates the risk. If every generated candidate is identical, extra sampling slots can't uncover an alternative correct solution. When temperature is zero, pass@5 collapses completely to pass@1.

deterministic-candidates-add-no-search.py
1identical_failed_candidates = [0] * 10 2identical_passing_candidates = [1] * 10 3 4for name, outcomes in [ 5 ("same failed candidate", identical_failed_candidates), 6 ("same passing candidate", identical_passing_candidates), 7]: 8 correct = sum(outcomes) 9 print(name, f"pass@1={pass_at_k(10, correct, 1):.1f}", f"pass@5={pass_at_k(10, correct, 5):.1f}")
Output
1same failed candidate pass@1=0.0 pass@5=0.0 2same passing candidate pass@1=1.0 pass@5=1.0

Serving engines can introduce nondeterminism from external system factors, so record the exact runtime flags. A degenerate deterministic output distribution remains compatible with independent draws: it simply yields the identical completion on every call. Duplicate completions alone don't prove a protocol violation; they remain in nnn and ccc. A larger kkk represents a larger compute expenditure, not an unearned model upgrade.

Another risk is subtler: code that passes basic unit tests can still harbor edge-case bugs or malicious side effects. Unit-test evaluation establishes functional correctness under that specific suite. It doesn't authorize executing untrusted code directly on host infrastructure.

HumanEval's authors executed model completions inside an isolated sandbox for that exact reason.[8]Reference 8Evaluating Large Language Models Trained on Code (HumanEval).https://arxiv.org/abs/2107.03374 Treat functional correctness testing and execution isolation as separate, indispensable layers.

Build an evaluation report

Pull the pieces together for a model-review scorecard. The 40-task paired fixture evaluates single-attempt lift. A separate four-task pool evaluates Model B's multi-candidate search behavior. Both require reported uncertainty and a documented protocol; the second fixture isn't a direct comparison with Model A.

This self-contained script runs the calculations and reports each fixture alongside its protocol metadata:

evaluation-report.py
1from itertools import product 2from math import prod 3from random import Random 4 5def pass_at_k(n: int, c: int, k: int) -> float: 6 if n <= 0 or not 0 <= c <= n or not 1 <= k <= n: 7 raise ValueError("require n > 0, 0 <= c <= n, and 1 <= k <= n") 8 if n - c < k: 9 return 1.0 10 return 1.0 - prod(1.0 - k / i for i in range(n - c + 1, n + 1)) 11 12def quantile(values: list[float], q: float) -> float: 13 ordered = sorted(values) 14 pos = (len(ordered) - 1) * q 15 lo = int(pos) 16 hi = min(lo + 1, len(ordered) - 1) 17 frac = pos - lo 18 return ordered[lo] * (1 - frac) + ordered[hi] * frac 19 20model_a = [1] * 17 + [0] * 10 + [0] * 8 + [1] * 5 21model_b = [1] * 17 + [0] * 10 + [1] * 8 + [0] * 5 22paired_differences = [b - a for a, b in zip(model_a, model_b)] 23paired_lift = sum(paired_differences) / len(paired_differences) 24rng = Random(7) 25lift_resamples = [ 26 sum(rng.choice(paired_differences) for _ in paired_differences) 27 / len(paired_differences) 28 for _ in range(20_000) 29] 30lift_lo, lift_hi = quantile(lift_resamples, 0.025), quantile(lift_resamples, 0.975) 31 32correct_counts_b = [0, 1, 2, 4] 33task_scores = [pass_at_k(10, correct, 5) for correct in correct_counts_b] 34pass5 = sum(task_scores) / len(task_scores) 35# Task-level bootstrap of macro pass@k: every with-replacement resample of the 36# four task scores (4^4 = 256), then percentile interval of the resample means. 37# With only four toy tasks the interval is huge; the point is the reporting contract. 38boot_means = [ 39 sum(task_scores[i] for i in idxs) / len(task_scores) 40 for idxs in product(range(len(task_scores)), repeat=len(task_scores)) 41] 42pass5_lo, pass5_hi = quantile(boot_means, 0.025), quantile(boot_means, 0.975) 43protocol = { 44 "paired_fixture_tasks": len(model_a), 45 "paired_candidates_per_model_per_task": 1, 46 "candidate_pool_fixture_tasks": len(correct_counts_b), 47 "metric": "hidden-test functional correctness", 48 "samples_per_task": 10, 49 "reported_k": 5, 50 "outcomes": "illustrative fixtures, not live generations", 51} 52 53print(f"paired pass@1 lift: {paired_lift * 100:+.1f} percentage points") 54print(f"approximate 95% lift interval: [{lift_lo * 100:+.1f}, {lift_hi * 100:+.1f}] points") 55print(f"Model B pass@5 on candidate pool: {pass5:.3f}") 56print(f"pass@5 95% task-bootstrap interval: [{pass5_lo:.3f}, {pass5_hi:.3f}]") 57for key, value in protocol.items(): 58 print(f"{key}: {value}")
Output
1paired pass@1 lift: +7.5 percentage points 2approximate 95% lift interval: [-10.0, +25.0] points 3Model B pass@5 on candidate pool: 0.563 4pass@5 95% task-bootstrap interval: [0.194, 0.877] 5paired_fixture_tasks: 40 6paired_candidates_per_model_per_task: 1 7candidate_pool_fixture_tasks: 4 8metric: hidden-test functional correctness 9samples_per_task: 10 10reported_k: 5 11outcomes: illustrative fixtures, not live generations

For k>1k > 1k>1, pass@k is a nonlinear function of task-level completion counts, which is subsequently averaged across tasks. Resampling tasks and recalculating macro pass@k models task-sampling uncertainty. Comparing Model A and Model B at the same kkk requires per-task estimates from both models: resample the paired differences between them, not two disconnected sets of tasks.

With four toy tasks the bootstrap interval is wide, but width alone doesn't guarantee nominal coverage. This demonstrates the calculation rather than providing enough evidence for a launch decision. This task bootstrap holds the generated completions fixed when resampling rows; it doesn't model how an entirely new batch of completions on the same tasks would vary. For grouped benchmark sets, resample independent clusters rather than pretending every prompt is independent.

Before approving a production rollout, verify that your evaluation report contains:

  • An interval for the paired lift rather than two disconnected headline numbers
  • The macro pass@k point estimate alongside an empirical bootstrap interval
  • The planned hypothesis direction chosen before inspecting test results
  • The total count of comparisons evaluated and any family-wise error adjustments
  • Explicit checkpoint hashes, prompt templates, decoding seeds, and timeout configs
  • Sandboxing guarantees and test suite coverage audits
  • Latency and cost ceilings for candidate generation in production

Statistical significance and operational value address different questions. Even ironclad statistical proof can't tell you whether a performance gain justifies additional candidate generation, evaluation overhead, execution latency, or security exposure.

Practice: review a benchmark claim

A teammate submits this update: "Model B is better because its pass@5 reached 64%, whereas Model A's pass@1 was 58%."

Write a code review comment addressing three specific issues:

  1. Request matched attempt budgets kkk, prompt suites, test suites, and decoding settings, while noting the sample size n≥kn \ge kn≥k.
  2. Ask for the paired lift and its confidence interval under that matched protocol.
  3. Inquire whether the extra generation budget, verification latency, and compute cost are viable for production serving.

A rigorous rewritten claim should read:

Under the same 200 paired platform-helper tasks and a matched pass@1 protocol, Model B's observed pass rate was 2.5 percentage points higher. The paired 95% interval and serving cost guardrails are reported below; the point estimate alone doesn't justify a production swap. pass@5 is documented separately because it reflects a larger candidate search budget.

Why is an interval for B - A preferable to comparing two separate intervals here?

Answer

The tasks are paired: both models solve the same prompts under the same tests. An interval for B - A preserves that pairing and directly measures the quantity the decision needs, which is the model lift.

Why can pass@5 be much higher than pass@1 without proving better single-candidate behavior?

Answer

pass@5 permits five candidate attempts per task. It answers whether at least one of five samples passes, while pass@1 evaluates one sample. The added search budget can raise the score.

Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.Two models are tested on the same six tasks. Model A outcomes are [1, 0, 1, 0, 1, 0] and Model B outcomes are [1, 1, 1, 0, 1, 0], where 1 means pass. Which conclusion is supported?

Correct answer: B's pass@1 is 4/6 versus A's 3/6, but only one task is a B-only win, so the evidence is weak.

The paired differences are [0, 1, 0, 0, 0, 0]. The ties still count in the pass rates, but they don't say which model wins a disagreement. With one disagreement and one B win, the one-sided fair-coin p-value is 0.5, so this isn't strong replacement evidence.

2.Why does a paired evaluation across shared prompts yield far lower variance than an unpaired evaluation across separate prompt batches?

Correct answer: Prompt difficulty covariance is positive, so the -2 Cov(A, B) term directly cancels between-prompt noise.

Because prompt difficulty varies across tasks, both models perform well on easy prompts and struggle on hard prompts, creating strong positive covariance Cov(A, B) > 0. In Var(B - A) = [Var(A) + Var(B) - 2 Cov(A, B)] / N, this covariance term cancels out the prompt difficulty variation.

3.A paired binary comparison has 16 disagreement tasks: B wins 13 and A wins 3. The claim 'B is better' was chosen before seeing the data. Under the null that either model is equally likely to win each disagreement, what one-sided p-value should be used?

Correct answer: sum_{w=13}^{16} C(16,w) / 2^16, about 0.0106 under the B=A null

Under the null, each disagreement is a fair coin between a B win and an A win. For the predeclared one-sided claim that B is better, the p-value is the probability of seeing at least 13 B wins out of 16 disagreements, not the observed win fraction or one exact sequence.

4.An evaluation report concludes p = 0.22 on a 30-task benchmark and claims 'Model A and Model B have identical performance.' What statistical error was committed?

Correct answer: Confusing absence of evidence with evidence of absence; the test may lack statistical power to detect a real difference.

A high p-value means the observed difference could easily occur by chance under the null; it does not prove the null hypothesis is true. With small sample sizes, statistical power is low, so genuine improvements often fail to achieve statistical significance.

5.A 40-task paired evaluation has 17 both pass, 10 both fail, 8 B-only passes, and 5 A-only passes. A paired bootstrap interval for B - A is -10.0 to +25.0 percentage points. Which procedure and conclusion match the paired design?

Correct answer: Resample complete task rows, recompute mean B - A, and call +7.5 points inconclusive because the interval crosses zero.

The observed paired lift is (8 - 5) / 40 = 0.075, or +7.5 percentage points. The bootstrap must keep each task row paired, then recompute the mean difference. Because the interval includes zero, plausible lifts include no gain or a loss, so the evidence is inconclusive.

6.For one HumanEval-style task, a model generated n = 10 candidate implementations and c = 2 passed hidden tests. What is the pass@5 estimate for selecting five candidates from that pool?

Correct answer: 1 - C(8, 5) / C(10, 5) = 0.778

Count the failure case. There are 8 failing candidates, C(10, 5) total groups of five, and C(8, 5) all-failing groups. The chance that a selected group contains at least one passing completion is 1 - C(8, 5) / C(10, 5) = 1 - 56 / 252 = 0.778.

7.For n = 10, c = 2, and k = 5, why is 1 - (1 - c/n)^k = 0.672 the wrong HumanEval-style report?

Correct answer: It samples the observed pool with replacement and underestimates 0.778.

The plug-in 1-(1-c/n)^k is a biased underestimate of generative pass@k due to Jensen's inequality on concave curves and sampling with replacement. HumanEval reports 1 - C(n-c, k)/C(n, k), which for these counts is 1 - 56/252 = 0.778.

8.Four tasks each have n = 10 candidates, with correct counts [0, 1, 2, 4]. How should the benchmark pass@5 score be computed?

Correct answer: Compute each task's pass@5, then average the unrounded estimates to obtain about 0.563.

The unrounded task estimates are 0, 0.5, about 0.777778, and about 0.976190. Their mean is about 0.563492, reported as 0.563 to three decimal places. Averaging task estimates gives every task equal benchmark weight; pooling candidate outcomes answers a different question.

9.A teammate writes, 'Model B is better because its pass@5 is 64%, while Model A's pass@1 is 58%.' Which review comment identifies the flaw and requests a fair comparison?

Correct answer: Match tasks, tests, k, and decoding; report n, paired lift, and uncertainty.

The claim compares different candidate-search budgets, so it doesn't isolate model quality. Match the tasks, tests, k, and decoding policy, then preserve task pairing when estimating uncertainty. Different fixed n values can estimate the same target when n is at least k, but they change precision and should be reported.

10.A paired-lift interval is [+1, +14] percentage points. Before the experiment, the team required a gain above two points to justify extra cost. Which conclusion follows?

Correct answer: The interval excludes zero but doesn't establish a gain above the two-point requirement.

Positive effect and useful effect are different questions. The interval still includes gains between one and two points, which would not meet the predeclared requirement. Frequentist coverage describes the interval procedure over repeated samples, not a posterior probability for this realized interval.

10 questions remaining.

Next Step
Continue to Neural Networks from Scratch

This lesson evaluated coding outcomes without opening the model. Next, trace a small CodeAssist timeout-risk network from two input features to a score. The forward pass explains how a network computes; the evaluation habits here explain what evidence you still need before trusting its outputs.

PreviousDistributions and Sampling
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

The Statistical Sign Test

Dixon, W. J. and Mood, A. M. · 1946 · Journal of the American Statistical Association

https://doi.org/10.1080/01621459.1946.10501898

ASA Statement on Statistical Significance and P-Values

American Statistical Association · 2016

https://www.amstat.org/asa/files/pdfs/P-ValueStatement.pdf

scipy.stats.binomtest

SciPy Developers · 2026

https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.binomtest.html

NIST/SEMATECH e-Handbook: Bonferroni's Method

NIST/SEMATECH · 2026

https://www.itl.nist.gov/div898/handbook/prc/section4/prc473.htm

NIST/SEMATECH e-Handbook: What Are Confidence Intervals?

NIST/SEMATECH · 2026

https://www.itl.nist.gov/div898/handbook/prc/section1/prc14.htm

Bootstrap Methods: Another Look at the Jackknife.

Efron, B. · 1979 · Annals of Statistics

https://projecteuclid.org/journals/annals-of-statistics/volume-7/issue-1/Bootstrap-Methods--Another-Look-at-the-Jackknife/10.1214/aos/1176344552.full

scipy.stats.bootstrap

SciPy Developers · 2026

https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.bootstrap.html

Evaluating Large Language Models Trained on Code (HumanEval).

Chen, M., et al. · 2021 · arXiv preprint

https://arxiv.org/abs/2107.03374

HumanEval: Functional Correctness Evaluation and pass@k Estimator

OpenAI · 2026

https://github.com/openai/human-eval/blob/master/human_eval/evaluation.py

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.