Files
mlc-ai--mlc-llm/tests/python/op/test_batch_spec_verify.py
wehub-resource-sync 770d92cb1f
Lint / lint (push) Waiting to run
Windows CI / Windows (push) Waiting to run
Build Docs / Deploy Docs (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:23:58 +08:00

164 lines
6.1 KiB
Python

import numpy as np
import pytest
import tvm
import tvm.testing
from mlc_llm.op.batch_spec_verify import batch_spec_verify
# test category "op_correctness"
pytestmark = [pytest.mark.op_correctness]
@pytest.mark.parametrize("nbatch", [32, 64])
@pytest.mark.parametrize("vocab", [3, 32, 64, 32000, 33, 65, 32001, 128000])
@pytest.mark.parametrize("plist", [[0.5, 0.5], [1, 0], [0, 1]])
def test_batch_spec_verify(nbatch, vocab, plist):
def numpy_reference(
draft_probs,
draft_tokens,
model_probs,
token_tree_first_child,
token_tree_next_sibling,
uniform_samples,
token_tree_parent_ptr,
):
nbatch = token_tree_parent_ptr.shape[0]
for b in range(nbatch):
parent_ptr = token_tree_parent_ptr[b]
child_ptr = token_tree_first_child[parent_ptr]
while child_ptr != -1:
child_token = draft_tokens[child_ptr]
p_child = model_probs[parent_ptr, child_token]
q_child = draft_probs[child_ptr, child_token]
uniform_sample = uniform_samples[child_ptr]
if p_child / q_child >= uniform_sample:
parent_ptr = child_ptr
child_ptr = token_tree_first_child[child_ptr]
else:
model_probs[parent_ptr, :] = np.maximum(
model_probs[parent_ptr, :] - draft_probs[child_ptr, :], 0.0
)
psum = np.sum(model_probs[parent_ptr, :])
model_probs[parent_ptr, :] /= psum
child_ptr = token_tree_next_sibling[child_ptr]
token_tree_parent_ptr[b] = parent_ptr
np.random.seed(0)
def gen_chain(num_nodes, base):
token_tree_first_child = list()
token_tree_next_sibling = list()
for i in range(num_nodes):
token_tree_first_child.append(base + i + 1 if i + 1 < num_nodes else -1)
token_tree_next_sibling.append(-1)
return token_tree_first_child, token_tree_next_sibling, base, base + 1
def gen_full_binary_tree(height, base):
token_tree_first_child = list()
token_tree_next_sibling = list()
num_nodes = 2**height - 1
for i in range(num_nodes):
token_tree_first_child.append(base + i * 2 + 1 if i * 2 + 1 < num_nodes else -1)
token_tree_next_sibling.append(base + i * 2 + 2 if i * 2 + 2 < num_nodes else -1)
return token_tree_first_child, token_tree_next_sibling, base, base + 1
### Inputs
num_nodes = 0
token_tree_first_child = list()
token_tree_next_sibling = list()
token_tree_parent_ptr = list()
for _ in range(nbatch):
choice = np.random.choice(2, 1, p=plist)
if choice == 0:
nodes_batch = np.random.randint(3, 32)
res = gen_chain(nodes_batch, num_nodes)
num_nodes += nodes_batch
else:
height = np.random.randint(3, 5)
res = gen_full_binary_tree(height, num_nodes)
num_nodes += 2**height - 1
token_tree_first_child.extend(res[0])
token_tree_next_sibling.extend(res[1])
token_tree_parent_ptr.append(res[2])
token_tree_first_child = np.array(token_tree_first_child).astype("int32")
token_tree_next_sibling = np.array(token_tree_next_sibling).astype("int32")
token_tree_parent_ptr = np.array(token_tree_parent_ptr).astype("int32")
draft_probs = np.random.rand(num_nodes, vocab).astype("float32")
draft_probs /= np.sum(draft_probs, axis=1, keepdims=True)
draft_tokens = np.random.randint(0, vocab, num_nodes).astype("int32")
model_probs = np.random.rand(num_nodes, vocab).astype("float32")
model_probs /= np.sum(model_probs, axis=1, keepdims=True)
uniform_samples = np.random.rand(num_nodes).astype("float32")
### TVM Inputs
dev = tvm.cuda(0)
draft_probs_tvm = tvm.runtime.tensor(draft_probs, dev)
draft_tokens_tvm = tvm.runtime.tensor(draft_tokens, dev)
model_probs_tvm = tvm.runtime.tensor(model_probs, dev)
token_tree_first_child_tvm = tvm.runtime.tensor(token_tree_first_child, dev)
token_tree_next_sibling_tvm = tvm.runtime.tensor(token_tree_next_sibling, dev)
uniform_samples_tvm = tvm.runtime.tensor(uniform_samples, dev)
token_tree_parent_ptr_tvm = tvm.runtime.tensor(token_tree_parent_ptr, dev)
# print("draft_probs", draft_probs)
# print("draft_tokens", draft_tokens)
# print("model_probs", model_probs)
# print("token_tree_first_child", token_tree_first_child)
# print("token_tree_next_sibling", token_tree_next_sibling)
# print("uniform_samples", uniform_samples)
# print("token_tree_parent_ptr", token_tree_parent_ptr)
### Numpy reference
numpy_reference(
draft_probs,
draft_tokens,
model_probs,
token_tree_first_child,
token_tree_next_sibling,
uniform_samples,
token_tree_parent_ptr,
)
# print("model_probs", model_probs)
# print("token_tree_parent_ptr", token_tree_parent_ptr)
### TVM
kernel = batch_spec_verify(vocab)
mod = tvm.build(kernel, target="cuda")
mod(
draft_probs_tvm,
draft_tokens_tvm,
model_probs_tvm,
token_tree_first_child_tvm,
token_tree_next_sibling_tvm,
uniform_samples_tvm,
token_tree_parent_ptr_tvm,
)
# print("model_probs", model_probs_tvm.asnumpy())
# print("token_tree_parent_ptr", token_tree_parent_ptr_tvm.asnumpy())
tvm.testing.assert_allclose(model_probs, model_probs_tvm.asnumpy())
tvm.testing.assert_allclose(
token_tree_parent_ptr, token_tree_parent_ptr_tvm.asnumpy(), rtol=0, atol=0
)
time_evaluator = mod.time_evaluator(mod.entry_name, dev, number=10, repeat=3)
print(f"batch_size: {nbatch}, vocab_size: {vocab}, tree_structure: {plist}")
print(
time_evaluator(
draft_probs_tvm,
draft_tokens_tvm,
model_probs_tvm,
token_tree_first_child_tvm,
token_tree_next_sibling_tvm,
uniform_samples_tvm,
token_tree_parent_ptr_tvm,
)
)
if __name__ == "__main__":
tvm.testing.main()