Text Generation
PyTorch
English
uraionspec
speculative-decoding
dspark
deepseek
llm-inference
model-optimization
transformer
efficient-llm
inference-acceleration
draft-model
torch
uraion-labs
uraion
systems-research
icml-2026
acceptance-scheduling
semi-autoregressive
confidence-prediction
calibration
| """Tests for the DFlash-style parallel backbone with KV injection.""" | |
| import torch | |
| import pytest | |
| from uraionspec.models.dflash_backbone import ( | |
| DFlashBackbone, | |
| DFlashDecoderLayer, | |
| DFlashAttention, | |
| DSparkAttentionMask, | |
| ) | |
| class TestDFlashAttention: | |
| """Test the target KV injection attention.""" | |
| def attn(self): | |
| return DFlashAttention(hidden_size=64, num_heads=4, dropout=0.0) | |
| def test_forward_shape(self, attn): | |
| B, L_draft, L_ctx, D = 2, 4, 8, 64 | |
| hidden = torch.randn(B, L_draft, D) | |
| target = torch.randn(B, L_ctx, D) | |
| out = attn(hidden, target) | |
| assert out.shape == (B, L_draft, D) | |
| def test_gqa_forward(self): | |
| """Test grouped query attention.""" | |
| attn = DFlashAttention(hidden_size=64, num_heads=4, num_kv_heads=2) | |
| B, L_draft, L_ctx, D = 2, 4, 8, 64 | |
| hidden = torch.randn(B, L_draft, D) | |
| target = torch.randn(B, L_ctx, D) | |
| out = attn(hidden, target) | |
| assert out.shape == (B, L_draft, D) | |
| def test_with_mask(self, attn): | |
| B, L_draft, L_ctx, D = 2, 4, 8, 64 | |
| hidden = torch.randn(B, L_draft, D) | |
| target = torch.randn(B, L_ctx, D) | |
| # Causal mask | |
| mask = torch.zeros(B, 1, L_draft, L_ctx + L_draft) | |
| mask[:, :, :, L_ctx:] = torch.triu( | |
| torch.full((L_draft, L_draft), float("-inf")), diagonal=1 | |
| ).unsqueeze(0).unsqueeze(0) | |
| out = attn(hidden, target, attention_mask=mask) | |
| assert out.shape == (B, L_draft, D) | |
| def test_gradient_flow(self, attn): | |
| B, L_draft, L_ctx, D = 1, 2, 4, 64 | |
| hidden = torch.randn(B, L_draft, D, requires_grad=True) | |
| target = torch.randn(B, L_ctx, D) | |
| out = attn(hidden, target) | |
| loss = out.sum() | |
| loss.backward() | |
| assert hidden.grad is not None | |
| assert hidden.grad.shape == (B, L_draft, D) | |
| class TestDFlashDecoderLayer: | |
| """Test a single DFlash decoder layer.""" | |
| def layer(self): | |
| return DFlashDecoderLayer( | |
| hidden_size=64, | |
| num_heads=4, | |
| intermediate_size=128, | |
| dropout=0.0, | |
| ) | |
| def test_forward_shape(self, layer): | |
| B, L_draft, L_ctx, D = 2, 4, 8, 64 | |
| hidden = torch.randn(B, L_draft, D) | |
| target = torch.randn(B, L_ctx, D) | |
| out = layer(hidden, target) | |
| assert out.shape == (B, L_draft, D) | |
| def test_residual_connection(self, layer): | |
| """Output should differ from input (non-identity transformation).""" | |
| B, L_draft, L_ctx, D = 1, 2, 4, 64 | |
| hidden = torch.randn(B, L_draft, D) | |
| target = torch.randn(B, L_ctx, D) | |
| with torch.no_grad(): | |
| out = layer(hidden, target) | |
| assert not torch.allclose(out, hidden, atol=1e-4) | |
| def test_all_activations(self): | |
| for act in ["gelu", "relu", "silu"]: | |
| layer = DFlashDecoderLayer( | |
| hidden_size=32, num_heads=2, intermediate_size=64, | |
| dropout=0.0, activation=act, | |
| ) | |
| B, L_draft, L_ctx = 1, 2, 4 | |
| hidden = torch.randn(B, L_draft, 32) | |
| target = torch.randn(B, L_ctx, 32) | |
| out = layer(hidden, target) | |
| assert out.shape == (B, L_draft, 32) | |
| class TestDFlashBackbone: | |
| """Test the full DFlash backbone stack.""" | |
| def backbone(self): | |
| return DFlashBackbone( | |
| hidden_size=64, | |
| num_layers=2, | |
| num_attention_heads=4, | |
| intermediate_size=128, | |
| dropout=0.0, | |
| ) | |
| def test_forward_shape(self, backbone): | |
| B, L_draft, L_ctx, D = 2, 4, 8, 64 | |
| hidden = torch.randn(B, L_draft, D) | |
| target = torch.randn(B, L_ctx, D) | |
| out = backbone(hidden, target) | |
| assert out.shape == (B, L_draft, D) | |
| def test_output_hidden_states(self, backbone): | |
| B, L_draft, L_ctx, D = 2, 4, 8, 64 | |
| hidden = torch.randn(B, L_draft, D) | |
| target = torch.randn(B, L_ctx, D) | |
| out, all_hidden = backbone(hidden, target, output_hidden_states=True) | |
| assert len(all_hidden) == 3 # input + 2 layers | |
| for h in all_hidden: | |
| assert h.shape == (B, L_draft, D) | |
| def test_gradient_flow(self, backbone): | |
| B, L_draft, L_ctx, D = 1, 3, 6, 64 | |
| hidden = torch.randn(B, L_draft, D, requires_grad=True) | |
| target = torch.randn(B, L_ctx, D) | |
| out = backbone(hidden, target) | |
| loss = out.sum() | |
| loss.backward() | |
| assert hidden.grad is not None | |
| def test_empty_draft(self, backbone): | |
| """Edge case: no draft tokens.""" | |
| B, L_ctx, D = 2, 8, 64 | |
| hidden = torch.randn(B, 0, D) | |
| target = torch.randn(B, L_ctx, D) | |
| out = backbone(hidden, target) | |
| assert out.shape == (B, 0, D) | |
| def test_single_draft_token(self, backbone): | |
| """Edge case: single draft token.""" | |
| B, L_ctx, D = 2, 8, 64 | |
| hidden = torch.randn(B, 1, D) | |
| target = torch.randn(B, L_ctx, D) | |
| out = backbone(hidden, target) | |
| assert out.shape == (B, 1, D) | |
| def test_many_layers(self): | |
| """Test with more layers.""" | |
| backbone = DFlashBackbone( | |
| hidden_size=32, num_layers=6, num_attention_heads=4, | |
| ) | |
| B, L_draft, L_ctx = 2, 4, 8 | |
| hidden = torch.randn(B, L_draft, 32) | |
| target = torch.randn(B, L_ctx, 32) | |
| out = backbone(hidden, target) | |
| assert out.shape == (B, L_draft, 32) | |
| class TestDSparkAttentionMask: | |
| """Test the custom DSpark attention mask builder.""" | |
| def test_mask_shape(self): | |
| B, seq_len = 2, 10 | |
| num_blocks, block_size = 3, 4 | |
| device = "cpu" | |
| mask = DSparkAttentionMask.create_dspark_attention_mask( | |
| batch_size=B, | |
| seq_len=seq_len, | |
| num_blocks=num_blocks, | |
| block_size=block_size, | |
| device=torch.device(device), | |
| ) | |
| L_draft = num_blocks * block_size | |
| assert mask.shape == (B, 1, L_draft, seq_len + L_draft) | |
| def test_context_attention(self): | |
| """Draft tokens should be able to attend to all context tokens.""" | |
| B, seq_len = 1, 5 | |
| num_blocks, block_size = 2, 3 | |
| device = "cpu" | |
| mask = DSparkAttentionMask.create_dspark_attention_mask( | |
| batch_size=B, seq_len=seq_len, | |
| num_blocks=num_blocks, block_size=block_size, | |
| device=torch.device(device), | |
| ) | |
| # All draft positions should have 0.0 for all context positions | |
| context_slice = mask[0, 0, :, :seq_len] | |
| assert (context_slice == 0.0).all() | |
| def test_intra_block_attention(self): | |
| """Draft tokens in the same block should attend to each other.""" | |
| B, seq_len = 1, 5 | |
| num_blocks, block_size = 2, 3 | |
| device = "cpu" | |
| mask = DSparkAttentionMask.create_dspark_attention_mask( | |
| batch_size=B, seq_len=seq_len, | |
| num_blocks=num_blocks, block_size=block_size, | |
| device=torch.device(device), | |
| ) | |
| L_ctx = seq_len | |
| # Block 0: positions 0,1,2 should attend to each other | |
| intra_block_0 = mask[0, 0, 0:3, L_ctx:L_ctx+3] | |
| assert (intra_block_0 == 0.0).all(), "Block 0 intra-attention should be 0" | |
| # Block 1: positions 3,4,5 should attend to each other | |
| intra_block_1 = mask[0, 0, 3:6, L_ctx+3:L_ctx+6] | |
| assert (intra_block_1 == 0.0).all(), "Block 1 intra-attention should be 0" | |
| def test_cross_block_no_attention(self): | |
| """Draft tokens should NOT attend to draft tokens in other blocks.""" | |
| B, seq_len = 1, 5 | |
| num_blocks, block_size = 2, 3 | |
| device = "cpu" | |
| mask = DSparkAttentionMask.create_dspark_attention_mask( | |
| batch_size=B, seq_len=seq_len, | |
| num_blocks=num_blocks, block_size=block_size, | |
| device=torch.device(device), | |
| ) | |
| L_ctx = seq_len | |
| # Block 0 should NOT attend to Block 1's draft tokens | |
| cross_block = mask[0, 0, 0:3, L_ctx+3:L_ctx+6] | |
| assert (cross_block == float("-inf")).all(), \ | |
| "Cross-block attention should be -inf" | |
| # Block 1 should NOT attend to Block 0's draft tokens | |
| cross_block_2 = mask[0, 0, 3:6, L_ctx:L_ctx+3] | |
| assert (cross_block_2 == float("-inf")).all(), \ | |
| "Cross-block attention should be -inf" | |