Files
enginex-ascend-910-vllm/tests/ut/sample/test_rejection_sampler.py
Sun Ruoxi 7f8a1b1f7a init v0.23.0
Signed-off-by: Sun Ruoxi <sunruoxi@4paradigm.com>
2026-08-27 15:11:51 +08:00

936 lines
32 KiB
Python

from unittest.mock import patch
import torch
from tests.ut.base import TestBase
from vllm_ascend.sample.rejection_sampler import (
expand_batch_to_tokens,
expand_pytorch,
rejection_greedy_sample_pytorch,
rejection_random_sample_block_verify_pytorch,
rejection_random_sample_pytorch,
sample_recovered_tokens_blockwise_pytorch,
sample_recovered_tokens_pytorch,
)
# Global constants
PLACEHOLDER_TOKEN_ID = -1
GREEDY_TEMPERATURE = 0.0
MAX_SPEC_LEN = 8 # Used as MAX_NUM_TOKENS in expand_batch_to_tokens
def mock_pin_memory(original_func):
def func_wo_pin_memory(*args, **kwargs):
if kwargs.get("pin_memory", False):
kwargs["pin_memory"] = False
return original_func(*args, **kwargs)
return func_wo_pin_memory
class TestAscendRejectionSampler(TestBase):
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_rejection_greedy_sample_pytorch(self):
"""Test greedy rejection sampling: stop when draft doesn't match, otherwise append bonus token"""
batch_size = 2
max_spec_len = 2
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2, 4])
num_draft_tokens = [2, 2]
draft_token_ids = torch.tensor([10, 11, 20, 21])
target_argmax = torch.tensor([10, 99, 20, 22])
bonus_token_ids = torch.tensor([[100], [200]])
is_greedy = torch.tensor([True, True])
rejection_greedy_sample_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
target_argmax,
bonus_token_ids,
num_draft_tokens,
max_spec_len,
is_greedy,
)
assert output_token_ids[0, 0].item() == 10
assert output_token_ids[0, 1].item() == 99
assert output_token_ids[1, 0].item() == 20
assert output_token_ids[1, 2].item() == PLACEHOLDER_TOKEN_ID
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_rejection_random_sample_pytorch(self):
"""Test random rejection sampling: accept based on uniform probability"""
batch_size = 2
max_spec_len = 3
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2, 1])
draft_token_ids = torch.tensor([1, 0, 2])
draft_probs = torch.tensor(
[
[0.0, 0.6, 0.0, 0.4], # vocab_size=4
[0.1, 0.2, 0.3, 0.4],
[0.5, 0.5, 0.0, 0.0],
]
)
target_probs = torch.tensor(
[
[0.0, 0.8, 0.0, 0.2],
[0.2, 0.1, 0.3, 0.4],
[0.9, 0.1, 0.0, 0.0],
]
)
bonus_token_ids = torch.tensor([[100], [200]])
recovered_token_ids = torch.tensor([1, 2, 3])
uniform_probs = torch.tensor([0.7, 0.6, 0.5])
is_greedy = torch.tensor([False, False])
vocab_size = 4
rejection_random_sample_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size,
IS_NGRAM=False,
)
assert output_token_ids[0, 0].item() == 1
assert output_token_ids[0, 1].item() == 0
assert output_token_ids[0, 2].item() == 100
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_rejection_random_sample_pytorch_rejects_placeholder(self):
batch_size = 1
max_spec_len = 1
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([1])
draft_token_ids = torch.tensor([PLACEHOLDER_TOKEN_ID])
target_probs = torch.tensor([[0.0, 0.0, 1.0]])
bonus_token_ids = torch.tensor([[100]])
recovered_token_ids = torch.tensor([2])
uniform_probs = torch.tensor([0.0])
is_greedy = torch.tensor([False])
rejection_random_sample_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
None,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size=3,
IS_NGRAM=True,
)
assert output_token_ids.tolist() == [[2, PLACEHOLDER_TOKEN_ID]]
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_rejection_random_sample_pytorch_rejects_all_placeholder_mtp3(self):
batch_size = 1
max_spec_len = 3
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([3])
draft_token_ids = torch.tensor([PLACEHOLDER_TOKEN_ID, PLACEHOLDER_TOKEN_ID, PLACEHOLDER_TOKEN_ID])
# Placeholder draft tokens must reject regardless of target probability.
# The recovered token is passed in after recovery sampling.
target_probs = torch.zeros((max_spec_len, 3))
bonus_token_ids = torch.tensor([[100]])
recovered_token_ids = torch.tensor([2, 1, 0])
uniform_probs = torch.tensor([0.0, 0.0, 0.0])
is_greedy = torch.tensor([False])
rejection_random_sample_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
None,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size=3,
IS_NGRAM=True,
)
assert output_token_ids.tolist() == [[2, PLACEHOLDER_TOKEN_ID, PLACEHOLDER_TOKEN_ID, PLACEHOLDER_TOKEN_ID]]
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_sample_recovered_tokens_pytorch_keeps_placeholder_distribution(self):
output_token_ids = torch.empty(1, dtype=torch.int32)
cu_num_draft_tokens = torch.tensor([1])
draft_token_ids = torch.tensor([PLACEHOLDER_TOKEN_ID])
target_probs = torch.tensor([[0.1, 0.2, 0.7]])
q = torch.ones((1, 3), dtype=torch.float32)
sample_recovered_tokens_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
None,
target_probs,
q,
vocab_size=3,
IS_NGRAM=True,
)
assert output_token_ids.tolist() == [2]
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_expand_pytorch(self):
"""Test expand_pytorch functionality"""
input_ptr = torch.tensor([10, 20, 30], dtype=torch.int32)
cu_num_tokens_ptr = torch.tensor([2, 5, 7])
output_ptr = torch.empty(7, dtype=torch.int32)
expand_pytorch(
output_ptr,
input_ptr,
cu_num_tokens_ptr,
replace_from=0,
replace_to=0,
MAX_NUM_TOKENS=MAX_SPEC_LEN,
)
expected = torch.tensor([10, 10, 20, 20, 20, 30, 30])
assert torch.equal(output_ptr, expected)
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_expand_batch_to_tokens(self):
"""Test expand_batch_to_tokens wrapper"""
x = torch.tensor([10, 20, 30])
cu_num_tokens = torch.tensor([2, 5, 7])
num_tokens = 7
# Test PyTorch path
with (
patch("vllm_ascend.sample.rejection_sampler.HAS_TRITON", False),
patch("vllm_ascend.sample.rejection_sampler.expand_pytorch") as mock_pytorch,
):
expand_batch_to_tokens(x, cu_num_tokens, num_tokens)
mock_pytorch.assert_called_once()
args = mock_pytorch.call_args[0]
assert (args[1] == x).all()
assert (args[2] == cu_num_tokens).all()
# Test Triton kernel path
with (
patch("vllm_ascend.sample.rejection_sampler.HAS_TRITON", True),
patch("vllm_ascend.sample.rejection_sampler.expand_triton") as mock_triton,
):
expand_batch_to_tokens(x, cu_num_tokens, num_tokens)
mock_triton.assert_called_once()
call_args = mock_triton.call_args[0]
assert (call_args[2] == x).all()
assert (call_args[3] == cu_num_tokens).all()
# Run actual function
with patch("vllm_ascend.sample.rejection_sampler.HAS_TRITON", False):
result = expand_batch_to_tokens(x, cu_num_tokens, num_tokens)
expected = torch.tensor([10, 10, 20, 20, 20, 30, 30])
assert torch.equal(result, expected)
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_sample_recovered_tokens_pytorch_ngram(self):
"""Test recovered token sampling under n-gram mode"""
output_token_ids = torch.empty(2, dtype=torch.int32)
cu_num_draft_tokens = torch.tensor([1, 2])
draft_token_ids = torch.tensor([1, 2])
draft_probs = None
target_probs = torch.tensor(
[
[0.1, 0.2, 0.7],
[0.3, 0.3, 0.4],
]
)
q = torch.tensor(
[
[0.1, 0.2, 0.7],
[0.5, 0.4, 0.1],
]
)
vocab_size = 3
sample_recovered_tokens_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
q,
vocab_size,
IS_NGRAM=True,
)
assert output_token_ids[0].item() == 0
assert output_token_ids[1].item() == 1
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_reduce_sample_recovered_tokens_pytorch_ngram(self):
"""Test recovered token sampling under n-gram mode"""
output_token_ids = torch.empty(2, dtype=torch.int32)
cu_num_draft_tokens = torch.tensor([1, 2])
draft_token_ids = torch.tensor([1, 2])
draft_probs = None
target_probs = torch.tensor(
[
[0.1, 0.2, 0.7],
[0.3, 0.3, 0.4],
]
)
q = torch.tensor(
[
[0.1, 0.2, 0.7],
[0.5, 0.4, 0.1],
]
)
vocab_size = 3
target_indices = torch.tensor(
[
[0, 1, 2],
[0, 1, 2],
]
)
enable_reduce_sampling = True
sample_recovered_tokens_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
q,
vocab_size,
IS_NGRAM=True,
target_indices=target_indices,
enable_reduce_sampling=enable_reduce_sampling,
)
assert output_token_ids[0].item() == 0
assert output_token_ids[1].item() == 1
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_rejection_random_reduce_sample_block_verify_pytorch(self):
"""Test random rejection sampling for block verify: accept based on uniform probability"""
batch_size = 2
max_spec_len = 3
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2, 1])
draft_token_ids = torch.tensor([1, 0, 2])
draft_probs = torch.tensor(
[
[0.0, 0.6, 0.0, 0.4, 0.0],
[0.1, 0.2, 0.3, 0.4, 0.0],
[0.5, 0.5, 0.0, 0.0, 0.0],
]
)
target_probs = torch.tensor(
[
[0.0, 0.8, 0.0, 0.2],
[0.2, 0.1, 0.3, 0.4],
[0.9, 0.1, 0.0, 0.0],
]
)
bonus_token_ids = torch.tensor([[100], [200]])
recovered_token_ids = torch.tensor([1, 2, 3])
uniform_probs = torch.tensor([0.7, 0.6, 0.5])
is_greedy = torch.tensor([False, False])
vocab_size = 5
target_indices = torch.tensor(
[
[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3],
]
)
enable_reduce_sampling = True
rejection_random_sample_block_verify_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size,
IS_NGRAM=False,
target_indices=target_indices,
enable_reduce_sampling=enable_reduce_sampling,
)
assert output_token_ids[0, 0].item() == 1
assert output_token_ids[0, 1].item() == 0
assert output_token_ids[0, 2].item() == 100
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_reduce_sample_recovered_tokens_blockwise_pytorch_ngram(self):
"""Test recovered token sampling for blockwise speculative decoding with n-gram."""
output_token_ids = torch.empty(2, dtype=torch.int32)
cu_num_draft_tokens = torch.tensor([1, 2])
draft_token_ids = torch.tensor([1, 2])
draft_probs = None
target_probs = torch.tensor(
[
[0.1, 0.2, 0.7],
[0.3, 0.3, 0.4],
]
)
q = torch.tensor(
[
[0.1, 0.2, 0.7],
[0.5, 0.4, 0.1],
]
)
vocab_size = 3
target_indices = torch.tensor(
[
[0, 1, 2],
[0, 1, 2],
]
)
enable_reduce_sampling = True
sample_recovered_tokens_blockwise_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
q,
vocab_size,
IS_NGRAM=True,
target_indices=target_indices,
enable_reduce_sampling=enable_reduce_sampling,
)
assert output_token_ids[0].item() == 0
assert output_token_ids[1].item() == 1
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_reduce_sample_recovered_tokens_blockwise_pytorch(self):
"""Test recovered token sampling for blockwise speculative decoding."""
output_token_ids = torch.empty(2, dtype=torch.int32)
cu_num_draft_tokens = torch.tensor([1, 2])
draft_token_ids = torch.tensor([0, 1])
draft_probs = torch.tensor(
[
[0.6, 0.1, 0.3],
[0.2, 0.7, 0.1],
]
)
target_probs = torch.tensor(
[
[0.8, 0.1, 0.1],
[0.3, 0.6, 0.1],
]
)
q = torch.tensor(
[
[0.5, 0.3, 0.2],
[0.1, 0.8, 0.1],
]
)
vocab_size = 3
target_indices = torch.tensor(
[
[0, 1, 2],
[0, 1, 2],
]
)
enable_reduce_sampling = True
sample_recovered_tokens_blockwise_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
q,
vocab_size,
IS_NGRAM=False,
target_indices=target_indices,
enable_reduce_sampling=enable_reduce_sampling,
)
assert output_token_ids[0].item() == 0
assert output_token_ids[1].item() == 0
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_rejection_random_sample_block_verify_pytorch_standard(self):
"""Test block verify without reduce_sampling: standard full-vocab path."""
batch_size = 2
max_spec_len = 3
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2, 3])
draft_token_ids = torch.tensor([1, 0, 2])
draft_probs = torch.tensor(
[
[0.0, 0.6, 0.0, 0.4],
[0.2, 0.0, 0.3, 0.5],
[0.0, 0.0, 0.5, 0.5],
]
)
target_probs = torch.tensor(
[
[0.0, 0.8, 0.0, 0.2],
[0.1, 0.0, 0.3, 0.6],
[0.0, 0.0, 0.9, 0.1],
]
)
bonus_token_ids = torch.tensor([[100], [200]])
recovered_token_ids = torch.tensor([99, 88, 77])
uniform_probs = torch.tensor([0.7, 0.6, 0.5])
is_greedy = torch.tensor([False, False])
vocab_size = 4
rejection_random_sample_block_verify_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size,
IS_NGRAM=False,
)
assert output_token_ids[0, 0].item() == 1
assert output_token_ids[0, 1].item() == 0
assert output_token_ids[0, 2].item() == 100
assert output_token_ids[1, 0].item() == 2
assert output_token_ids[1, 1].item() == 200
class TestEntropyVerify(TestBase):
"""Test ENTROPY_VERIFY mode in rejection sampling.
Entropy verify modifies the acceptance threshold based on the entropy
of the original target distribution:
- High entropy (uncertain) → lower effective threshold → more accepting
- Low entropy (certain) → higher effective threshold → stricter
"""
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_entropy_verify_standard_high_entropy_accepts_more(self):
"""High entropy (uniform-like) makes acceptance easier via lower threshold."""
batch_size = 2
max_spec_len = 2
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2, 1])
draft_token_ids = torch.tensor([1, 0, 2])
draft_probs = torch.tensor(
[
[0.6, 0.4, 0.0],
[0.2, 0.8, 0.0],
[0.5, 0.5, 0.0],
]
)
target_probs = torch.tensor(
[
[0.8, 0.2, 0.0],
[0.1, 0.9, 0.0],
[0.9, 0.1, 0.0],
]
)
bonus_token_ids = torch.tensor([[100], [200]])
recovered_token_ids = torch.tensor([99, 88, 77])
uniform_probs = torch.tensor([0.7, 0.6, 0.5])
is_greedy = torch.tensor([False, False])
vocab_size = 3
ori_target_probs = torch.tensor(
[
[0.8, 0.19, 0.01],
[0.09, 0.9, 0.01],
[0.9, 0.09, 0.01],
]
)
rejection_random_sample_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size,
IS_NGRAM=False,
ENTROPY_VERIFY=True,
POSTERIOR_THRESHOLD=0.95,
POSTERIOR_ALPHA=0.4,
EPSILON=1e-10,
ori_target_probs=ori_target_probs,
)
assert output_token_ids[0, 0].item() == 99
assert output_token_ids[0, 1].item() == -1
assert output_token_ids[0, 2].item() == -1
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_entropy_verify_standard_low_entropy_stricter(self):
"""Low entropy (peaked distribution) keeps threshold near POSTERIOR_THRESHOLD."""
batch_size = 1
max_spec_len = 2
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2])
draft_token_ids = torch.tensor([1, 0])
draft_probs = torch.tensor(
[
[0.6, 0.4, 0.0],
[0.8, 0.2, 0.0],
]
)
target_probs = torch.tensor(
[
[0.8, 0.2, 0.0],
[0.1, 0.9, 0.0],
]
)
bonus_token_ids = torch.tensor([[100]])
recovered_token_ids = torch.tensor([99, 88])
uniform_probs = torch.tensor([0.7, 0.6])
is_greedy = torch.tensor([False])
vocab_size = 3
ori_target_probs = torch.tensor(
[
[0.8, 0.19, 0.01],
[0.09, 0.9, 0.01],
]
)
rejection_random_sample_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size,
IS_NGRAM=False,
ENTROPY_VERIFY=True,
POSTERIOR_THRESHOLD=0.95,
POSTERIOR_ALPHA=0.4,
EPSILON=1e-10,
ori_target_probs=ori_target_probs,
)
assert output_token_ids[0, 0].item() == 99
assert output_token_ids[0, 1].item() == -1
assert output_token_ids[0, 2].item() == -1
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_entropy_verify_block_verify(self):
"""Entropy verify with block verify mode."""
batch_size = 2
max_spec_len = 3
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2, 1])
draft_token_ids = torch.tensor([1, 0, 2])
draft_probs = torch.tensor(
[
[0.6, 0.4, 0.0, 0.0],
[0.2, 0.8, 0.0, 0.0],
[0.5, 0.5, 0.0, 0.0],
]
)
target_probs = torch.tensor(
[
[0.8, 0.2, 0.0, 0.0],
[0.1, 0.9, 0.0, 0.0],
[0.9, 0.1, 0.0, 0.0],
]
)
bonus_token_ids = torch.tensor([[100], [200]])
recovered_token_ids = torch.tensor([99, 88, 77])
uniform_probs = torch.tensor([0.7, 0.6, 0.5])
is_greedy = torch.tensor([False, False])
vocab_size = 4
ori_target_probs = torch.tensor(
[
[0.8, 0.18, 0.01, 0.01],
[0.88, 0.9, 0.01, 0.01],
[0.9, 0.08, 0.01, 0.01],
]
)
rejection_random_sample_block_verify_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size,
IS_NGRAM=False,
ENTROPY_VERIFY=True,
POSTERIOR_THRESHOLD=0.95,
POSTERIOR_ALPHA=0.4,
EPSILON=1e-10,
ori_target_probs=ori_target_probs,
)
assert output_token_ids[0, 0].item() == 99
assert output_token_ids[0, 1].item() == -1
assert output_token_ids[0, 2].item() == -1
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_entropy_verify_ngram(self):
"""ENTROPY_VERIFY with IS_NGRAM: draft_probs=None, draft_token_probs=1.0.
In NGRAM mode, acceptance depends on target_prob alone (since
draft_prob=1.0). Entropy verify lowers the threshold for high-entropy
tokens, making acceptance easier when the target distribution is
uncertain.
"""
batch_size = 1
max_spec_len = 2
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2])
draft_token_ids = torch.tensor([0, 1])
draft_probs = None
target_probs = torch.tensor(
[
[0.6, 0.2, 0.2],
[0.1, 0.1, 0.8],
]
)
bonus_token_ids = torch.tensor([[100]])
recovered_token_ids = torch.tensor([99, 88])
uniform_probs = torch.tensor([0.7, 0.6])
is_greedy = torch.tensor([False])
vocab_size = 3
ori_target_probs = torch.tensor(
[
[0.6, 0.2, 0.2],
[0.1, 0.1, 0.8],
]
)
rejection_random_sample_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size,
IS_NGRAM=True,
ENTROPY_VERIFY=True,
POSTERIOR_THRESHOLD=0.95,
POSTERIOR_ALPHA=0.4,
EPSILON=1e-10,
ori_target_probs=ori_target_probs,
)
assert output_token_ids[0, 0].item() == 0
assert output_token_ids[0, 1].item() == 88
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_entropy_verify_block_verify_ngram(self):
"""ENTROPY_VERIFY + IS_NGRAM + block_verify combined.
Tests the interaction of all three modes: NGRAM (draft_probs=None),
block verify (cumulative acceptance), and entropy-based threshold
adjustment.
"""
batch_size = 1
max_spec_len = 3
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2])
draft_token_ids = torch.tensor([0, 1])
draft_probs = None
target_probs = torch.tensor(
[
[0.6, 0.2, 0.2, 0.0],
[0.1, 0.1, 0.8, 0.0],
]
)
bonus_token_ids = torch.tensor([[100]])
recovered_token_ids = torch.tensor([99, 88])
uniform_probs = torch.tensor([0.7, 0.6])
is_greedy = torch.tensor([False])
vocab_size = 4
ori_target_probs = torch.tensor(
[
[0.6, 0.2, 0.2, 0.0],
[0.1, 0.1, 0.8, 0.0],
]
)
rejection_random_sample_block_verify_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size,
IS_NGRAM=True,
ENTROPY_VERIFY=True,
POSTERIOR_THRESHOLD=0.95,
POSTERIOR_ALPHA=0.4,
EPSILON=1e-10,
ori_target_probs=ori_target_probs,
)
assert output_token_ids[0, 0].item() == 0
assert output_token_ids[0, 1].item() == 88
@patch("torch.arange", new=mock_pin_memory(torch.arange))
@patch("torch.ones", new=mock_pin_memory(torch.ones))
@patch("torch.full", new=mock_pin_memory(torch.full))
@patch("torch.tensor", new=mock_pin_memory(torch.tensor))
def test_entropy_verify_no_ori_probs_fallback(self):
"""When ori_target_probs is None, fallback to target_probs for entropy."""
batch_size = 1
max_spec_len = 2
output_token_ids = torch.full((batch_size, max_spec_len + 1), PLACEHOLDER_TOKEN_ID)
cu_num_draft_tokens = torch.tensor([2])
draft_token_ids = torch.tensor([1, 0])
draft_probs = torch.tensor(
[
[0.6, 0.4, 0.0],
[0.8, 0.2, 0.0],
]
)
target_probs = torch.tensor(
[
[0.35, 0.33, 0.32],
[0.34, 0.34, 0.32],
]
)
bonus_token_ids = torch.tensor([[100]])
recovered_token_ids = torch.tensor([99, 88])
uniform_probs = torch.tensor([0.7, 0.6])
is_greedy = torch.tensor([False])
vocab_size = 3
rejection_random_sample_pytorch(
output_token_ids,
cu_num_draft_tokens,
draft_token_ids,
draft_probs,
target_probs,
bonus_token_ids,
recovered_token_ids,
uniform_probs,
is_greedy,
max_spec_len,
vocab_size,
IS_NGRAM=False,
ENTROPY_VERIFY=True,
POSTERIOR_THRESHOLD=0.95,
POSTERIOR_ALPHA=0.4,
EPSILON=1e-10,
ori_target_probs=None,
)
assert output_token_ids[0, 0].item() == 1
assert output_token_ids[0, 1].item() in (0, 88)
assert output_token_ids[0, 2].item() == 100