Skip to content

Commit 3a51ee3

Browse files
[feat] support for iluvatar hardware backend (#58)
### PR Category Vendor ### PR Types New Features ### PR Description Support Qwen3-next on iluvatar hardware backend
1 parent c056155 commit 3a51ee3

11 files changed

Lines changed: 498 additions & 1 deletion

File tree

requirements_iluvatar.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#vllm==0.13.0
2+
decorator
3+
pyyaml
4+
scipy
5+
setuptools <= 79.0.1
6+
setuptools-scm
7+
scikit-build-core==0.11
8+
pybind11
9+
ninja
10+
cmake
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
"""
4+
ILUVATAR backend for vllm-plugin-FL dispatch.
5+
"""
6+
7+
from .iluvatar import IluvatarBackend
8+
9+
__all__ = ["IluvatarBackend"]
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
"""
4+
ILUVATAR backend implementation.
5+
6+
This backend provides operator implementations for Iluvatar GPUs.
7+
Iluvatar uses a CUDA-compatible architecture.
8+
"""
9+
10+
from __future__ import annotations
11+
12+
from typing import Optional, Union
13+
14+
import torch
15+
16+
from vllm_fl.dispatch.backends.base import Backend
17+
18+
19+
class IluvatarBackend(Backend):
20+
"""
21+
Iluvatar backend for operator implementations.
22+
23+
This backend uses Iluvatar libraries to provide high-performance
24+
operator implementations for Iluvatar GPUs.
25+
"""
26+
27+
_available: Optional[bool] = None
28+
29+
@property
30+
def name(self) -> str:
31+
return "iluvatar"
32+
33+
@property
34+
def vendor(self) -> Optional[str]:
35+
return "iluvatar"
36+
37+
def is_available(self) -> bool:
38+
"""
39+
Check if Iluvatar hardware and libraries are available.
40+
41+
This method uses the platform's vendor information to determine
42+
if the device is an Iluvatar GPU.
43+
"""
44+
if IluvatarBackend._available is None:
45+
try:
46+
from vllm.platforms import current_platform
47+
# Iluvatar GPUs should be detected via vendor_name
48+
if hasattr(current_platform, 'vendor_name') and current_platform.vendor_name == "iluvatar":
49+
IluvatarBackend._available = True
50+
else:
51+
# Fallback: check if CUDA is available with iluvatar device
52+
if torch.cuda.is_available():
53+
# Try to detect Iluvatar GPU
54+
# Iluvatar GPUs typically expose CUDA-compatible interface
55+
# We can check device name if available
56+
device_name = torch.cuda.get_device_name(0)
57+
if "iluvatar" in device_name.lower():
58+
IluvatarBackend._available = True
59+
else:
60+
IluvatarBackend._available = False
61+
62+
else:
63+
IluvatarBackend._available = False
64+
except Exception:
65+
IluvatarBackend._available = False
66+
return IluvatarBackend._available
67+
68+
# ==================== Operator Implementations ====================
69+
70+
def silu_and_mul(self, obj, x: torch.Tensor) -> torch.Tensor:
71+
"""
72+
SiLU activation followed by element-wise multiplication.
73+
74+
Args:
75+
obj: The calling obj (for interface consistency)
76+
x: Input tensor of shape [..., 2*d]
77+
78+
Returns:
79+
Output tensor of shape [..., d]
80+
"""
81+
from .impl.activation import silu_and_mul_iluvatar
82+
83+
return silu_and_mul_iluvatar(obj, x)
84+
85+
def rms_norm(
86+
self,
87+
obj,
88+
x: torch.Tensor,
89+
residual: Optional[torch.Tensor] = None,
90+
) -> Union[torch.Tensor, tuple[torch.Tensor, torch.Tensor]]:
91+
"""
92+
RMS normalization.
93+
94+
Args:
95+
obj: The calling obj (e.g., RMSNorm layer)
96+
x: Input tensor
97+
residual: Optional residual tensor
98+
99+
Returns:
100+
Normalized tensor, or tuple of (normalized, residual) if residual is provided
101+
"""
102+
from .impl.normalization import rms_norm_iluvatar
103+
104+
return rms_norm_iluvatar(obj, x, residual)
105+
106+
def rotary_embedding(
107+
self,
108+
obj,
109+
query: torch.Tensor,
110+
key: torch.Tensor,
111+
cos: torch.Tensor,
112+
sin: torch.Tensor,
113+
position_ids: torch.Tensor,
114+
rotary_interleaved: bool = False,
115+
inplace: bool = True,
116+
) -> tuple[torch.Tensor, torch.Tensor]:
117+
"""
118+
Apply rotary position embedding.
119+
120+
Args:
121+
obj: The calling obj (for interface consistency)
122+
query: Query tensor
123+
key: Key tensor
124+
cos: Cosine cache
125+
sin: Sine cache
126+
position_ids: Position indices
127+
rotary_interleaved: Whether to use interleaved rotary
128+
inplace: Whether to modify tensors in-place
129+
130+
Returns:
131+
Tuple of (embedded_query, embedded_key)
132+
"""
133+
from .impl.rotary import rotary_embedding_iluvatar
134+
135+
return rotary_embedding_iluvatar(
136+
obj,
137+
query,
138+
key,
139+
cos,
140+
sin,
141+
position_ids,
142+
rotary_interleaved=rotary_interleaved,
143+
inplace=inplace,
144+
)
145+
146+
def attention_backend(self, use_mla: bool = False) -> str:
147+
"""
148+
Get the attention backend class path for Iluvatar.
149+
150+
Args:
151+
use_mla: Whether to use Multi-head Latent Attention (MLA)
152+
153+
Returns:
154+
Fully qualified class path string
155+
"""
156+
from vllm.attention.backends.registry import AttentionBackendEnum
157+
158+
if use_mla:
159+
return AttentionBackendEnum.FLASHMLA.get_path()
160+
161+
return AttentionBackendEnum.FLASH_ATTN.get_path()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
"""
4+
ILUVATAR operator implementations.
5+
"""
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
"""
4+
Reference activation operator implementations using PyTorch.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
import torch
10+
import torch.nn.functional as F
11+
12+
13+
def silu_and_mul_iluvatar(obj, x: torch.Tensor) -> torch.Tensor:
14+
"""
15+
SiLU activation followed by element-wise multiplication using PyTorch.
16+
17+
Args:
18+
obj: The calling obj (for interface consistency)
19+
x: Input tensor of shape [..., 2*d]
20+
21+
Returns:
22+
Output tensor of shape [..., d]
23+
"""
24+
d = x.shape[-1] // 2
25+
x1, x2 = x[..., :d], x[..., d:]
26+
return F.silu(x1) * x2
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
"""
4+
Reference normalization operator implementations using PyTorch.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from typing import Optional, Union
10+
11+
import torch
12+
13+
14+
def rms_norm_iluvatar(
15+
obj,
16+
x: torch.Tensor,
17+
residual: Optional[torch.Tensor] = None,
18+
) -> Union[torch.Tensor, tuple[torch.Tensor, torch.Tensor]]:
19+
"""
20+
RMS normalization using PyTorch.
21+
22+
Args:
23+
obj: The calling obj (e.g., RMSNorm layer)
24+
x: Input tensor
25+
residual: Optional residual tensor
26+
27+
Returns:
28+
Normalized tensor, or tuple of (normalized, residual) if residual is provided
29+
"""
30+
# Get weight and epsilon from obj
31+
weight = obj.weight
32+
epsilon = obj.variance_epsilon
33+
34+
if residual is not None:
35+
x = x + residual
36+
residual = x
37+
38+
variance = x.pow(2).mean(-1, keepdim=True)
39+
x = x * torch.rsqrt(variance + epsilon)
40+
output = weight * x
41+
42+
if residual is not None:
43+
return output, residual
44+
return output
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copyright (c) 2026 BAAI. All rights reserved.
2+
3+
"""
4+
ILUVATAR rotary embedding operator implementations.
5+
6+
NOTE: This is a template/stub implementation using PyTorch reference code.
7+
Replace with actual Iluvatar-optimized implementations when available.
8+
"""
9+
10+
from __future__ import annotations
11+
12+
import torch
13+
14+
15+
def rotary_embedding_iluvatar(
16+
obj,
17+
query: torch.Tensor,
18+
key: torch.Tensor,
19+
cos: torch.Tensor,
20+
sin: torch.Tensor,
21+
position_ids: torch.Tensor,
22+
rotary_interleaved: bool = False,
23+
inplace: bool = True,
24+
) -> tuple[torch.Tensor, torch.Tensor]:
25+
"""
26+
Apply rotary position embedding using Iluvatar.
27+
28+
This is a placeholder implementation using PyTorch reference code.
29+
TODO: Replace with actual Iluvatar GPU optimized implementation.
30+
31+
Args:
32+
obj: The calling obj (for interface consistency)
33+
query: Query tensor [batch, num_heads, seq_len, head_dim] or [seq_len, num_heads, head_dim]
34+
key: Key tensor [batch, num_heads, seq_len, head_dim] or [seq_len, num_heads, head_dim]
35+
cos: Cosine cache [max_seq_len, rotary_dim] where rotary_dim = head_dim or head_dim // 2
36+
sin: Sine cache [max_seq_len, rotary_dim] where rotary_dim = head_dim or head_dim // 2
37+
position_ids: Position indices [batch, seq_len] or [seq_len]
38+
rotary_interleaved: Whether to use interleaved rotary
39+
inplace: Whether to modify tensors in-place (ignored in reference impl)
40+
41+
Returns:
42+
Tuple of (embedded_query, embedded_key)
43+
"""
44+
# Get cos/sin for the positions
45+
# position_ids can be [batch, seq_len] or [seq_len]
46+
if position_ids.dim() == 1:
47+
# [seq_len] -> [seq_len, rotary_dim]
48+
cos_selected = cos[position_ids]
49+
sin_selected = sin[position_ids]
50+
else:
51+
# [batch, seq_len] -> [batch, seq_len, rotary_dim]
52+
cos_selected = cos[position_ids]
53+
sin_selected = sin[position_ids]
54+
55+
# Expand dimensions to match query/key shape
56+
# query/key: [batch, num_heads, seq_len, head_dim] or [seq_len, num_heads, head_dim]
57+
if query.dim() == 4:
58+
# [batch, num_heads, seq_len, head_dim]
59+
# cos_selected: [batch, seq_len, rotary_dim] -> [batch, 1, seq_len, rotary_dim]
60+
cos_selected = cos_selected.unsqueeze(1)
61+
sin_selected = sin_selected.unsqueeze(1)
62+
elif query.dim() == 3:
63+
# [seq_len, num_heads, head_dim]
64+
# cos_selected: [seq_len, rotary_dim] -> [seq_len, 1, rotary_dim]
65+
cos_selected = cos_selected.unsqueeze(1)
66+
sin_selected = sin_selected.unsqueeze(1)
67+
68+
# Check if we need to repeat cos/sin to match head_dim
69+
rotary_dim = cos_selected.shape[-1]
70+
head_dim = query.shape[-1]
71+
72+
if rotary_dim != head_dim:
73+
# cos/sin only covers half of head_dim, need to repeat
74+
# This handles the case where rotary is only applied to part of the dimensions
75+
cos_selected = torch.cat([cos_selected, cos_selected], dim=-1)
76+
sin_selected = torch.cat([sin_selected, sin_selected], dim=-1)
77+
78+
def rotate_half(x):
79+
"""Rotates half the hidden dims of the input."""
80+
x1 = x[..., : x.shape[-1] // 2]
81+
x2 = x[..., x.shape[-1] // 2 :]
82+
return torch.cat((-x2, x1), dim=-1)
83+
84+
if rotary_interleaved:
85+
# Interleaved rotary
86+
def rotate_interleaved(x):
87+
x1 = x[..., ::2]
88+
x2 = x[..., 1::2]
89+
return torch.stack((-x2, x1), dim=-1).flatten(-2)
90+
91+
q_embed = (query * cos_selected) + (rotate_interleaved(query) * sin_selected)
92+
k_embed = (key * cos_selected) + (rotate_interleaved(key) * sin_selected)
93+
else:
94+
# Standard rotary (neox style)
95+
q_embed = (query * cos_selected) + (rotate_half(query) * sin_selected)
96+
k_embed = (key * cos_selected) + (rotate_half(key) * sin_selected)
97+
98+
return q_embed, k_embed

0 commit comments

Comments
 (0)