Skip to content

Commit 7192eb8

Browse files
CoyeCHENShawnsYing
andauthored
[KernelGen][Nvidia] Add special_i1e operator with Triton kernel (flagos-ai#5276)
Co-authored-by: shawn <chanceying@foxmail.com>
1 parent 6210042 commit 7192eb8

6 files changed

Lines changed: 188 additions & 0 deletions

File tree

benchmark/test_special_i1e.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright 2026 FlagOS Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pytest
16+
import torch
17+
18+
import flag_gems
19+
20+
from . import base, consts
21+
22+
23+
@pytest.mark.special_i1e
24+
@pytest.mark.skipif(
25+
flag_gems.vendor_name == "tsingmicro", reason="Issue #4131: not working"
26+
)
27+
def test_special_i1e():
28+
bench = base.UnaryPointwiseBenchmark(
29+
op_name="special_i1e", torch_op=torch.special.i1e, dtypes=consts.FLOAT_DTYPES
30+
)
31+
bench.run()

conf/operators.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9674,6 +9674,20 @@ ops:
96749674
stages:
96759675
- beta: '5.0'
96769676
- stable: '5.3'
9677+
- id: special_i1e
9678+
description: |
9679+
Computes the exponentially scaled modified Bessel function of the first kind
9680+
of order 1, i1e(x) = i1(x) * exp(-|x|), for each element in the input tensor.
9681+
for:
9682+
- special_i1e
9683+
labels:
9684+
- aten
9685+
- pointwise
9686+
- KernelGen
9687+
kind:
9688+
- Math
9689+
stages:
9690+
- alpha: '5.4'
96779691
- id: special_legendre_polynomial_p
96789692
description: |
96799693
Computes the Legendre polynomial P_n(x) of degree n for each element in the input tensor,

src/flag_gems/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ def torch_ge(v):
888888
("special_i0e_out", special_i0e_out),
889889
("special_i1", special_i1),
890890
("special_i1_out", special_i1_out),
891+
("special_i1e", special_i1e),
891892
("special_legendre_polynomial_p", special_legendre_polynomial_p),
892893
("special_log1p", special_log1p),
893894
("special_log1p.out", special_log1p_out),

src/flag_gems/ops/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@
646646
from flag_gems.ops.special_hermite_polynomial_h import special_hermite_polynomial_h
647647
from flag_gems.ops.special_i0e import special_i0e, special_i0e_out
648648
from flag_gems.ops.special_i1 import special_i1, special_i1_out
649+
from flag_gems.ops.special_i1e import special_i1e
649650
from flag_gems.ops.special_legendre_polynomial_p import special_legendre_polynomial_p
650651
from flag_gems.ops.special_log1p import special_log1p, special_log1p_out
651652
from flag_gems.ops.special_log_softmax import special_log_softmax
@@ -1504,6 +1505,7 @@
15041505
"special_i0e_out",
15051506
"special_i1",
15061507
"special_i1_out",
1508+
"special_i1e",
15071509
"special_legendre_polynomial_p",
15081510
"special_log_softmax",
15091511
"special_log1p",

src/flag_gems/ops/special_i1e.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Copyright 2026, The FlagOS Contributors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# Generated by KernelGen: https://github.com/flagos-ai/KernelGen
16+
import logging
17+
18+
import torch
19+
import triton
20+
import triton.language as tl
21+
22+
import flag_gems
23+
24+
logger = logging.getLogger(__name__)
25+
26+
27+
@triton.jit
28+
def _special_i1e_kernel(x_ptr, out_ptr, n_elements, BLOCK_SIZE: tl.constexpr):
29+
pid = tl.program_id(axis=0)
30+
block_start = pid * BLOCK_SIZE
31+
offsets = block_start + tl.arange(0, BLOCK_SIZE)
32+
mask = offsets < n_elements
33+
34+
x = tl.load(x_ptr + offsets, mask=mask, other=0.0)
35+
36+
# Compute in fp32 for accuracy/stability.
37+
# i1e(x) = i1(x) * exp(-|x|). Fold the exponential scaling directly into
38+
# the Cephes I1 approximation so the large-|x| branch never overflows.
39+
xf = x.to(tl.float32)
40+
ax = tl.abs(xf)
41+
42+
# Small region: |x| <= 3.75. I1(x) = x * P(y^2), y = x / 3.75.
43+
y = xf / 3.75
44+
y2 = y * y
45+
p = 0.00032411
46+
p = 0.00301532 + y2 * p
47+
p = 0.02658733 + y2 * p
48+
p = 0.15084934 + y2 * p
49+
p = 0.51498869 + y2 * p
50+
p = 0.87890594 + y2 * p
51+
p = 0.5 + y2 * p
52+
small = xf * p * tl.exp(-ax)
53+
54+
# Large region: |x| > 3.75. I1(x) ~ exp(|x|)/sqrt(|x|) * Q(3.75/|x|).
55+
# Multiplying by exp(-|x|) cancels the exponential prefactor.
56+
t = 3.75 / tl.maximum(ax, 1e-20)
57+
q = -0.00420059
58+
q = 0.01787654 + t * q
59+
q = -0.02895312 + t * q
60+
q = 0.02282967 + t * q
61+
q = -0.01031555 + t * q
62+
q = 0.00163801 + t * q
63+
q = -0.00362018 + t * q
64+
q = -0.03988024 + t * q
65+
q = 0.39894228 + t * q
66+
large = q / tl.sqrt(tl.maximum(ax, 1e-20))
67+
# I1 is odd, so i1e is odd as well.
68+
large = tl.where(xf < 0, -large, large)
69+
70+
y_out = tl.where(ax <= 3.75, small, large)
71+
tl.store(out_ptr + offsets, y_out.to(x.dtype), mask=mask)
72+
73+
74+
def _run_special_i1e_kernel(x: torch.Tensor, out: torch.Tensor):
75+
if x.device.type != flag_gems.device or out.device.type != flag_gems.device:
76+
raise ValueError(f"Tensors must be {flag_gems.device} tensors")
77+
assert x.dtype in (
78+
torch.float16,
79+
torch.bfloat16,
80+
torch.float32,
81+
torch.float64,
82+
), "Unsupported dtype"
83+
assert out.dtype == x.dtype, "Output dtype must match input dtype"
84+
85+
x_c = x.contiguous()
86+
out_c = out.contiguous()
87+
88+
n_elements = out_c.numel()
89+
if n_elements == 0:
90+
return out
91+
92+
grid = lambda meta: (triton.cdiv(n_elements, meta["BLOCK_SIZE"]),)
93+
_special_i1e_kernel[grid](x_c, out_c, n_elements, BLOCK_SIZE=1024)
94+
95+
if out_c.data_ptr() != out.data_ptr():
96+
out.copy_(out_c)
97+
return out
98+
99+
100+
def special_i1e(x: torch.Tensor):
101+
"""
102+
ATen wrapper: special_i1e(Tensor self) -> Tensor
103+
"""
104+
logger.debug("GEMS SPECIAL_I1E")
105+
out = torch.empty_like(x)
106+
return _run_special_i1e_kernel(x, out)

tests/test_special_i1e.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2026 FlagOS Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pytest
16+
import torch
17+
18+
import flag_gems
19+
20+
from . import accuracy_utils as utils
21+
22+
23+
@pytest.mark.special_i1e
24+
@pytest.mark.parametrize("shape", utils.POINTWISE_SHAPES)
25+
@pytest.mark.parametrize("dtype", utils.FLOAT_DTYPES)
26+
def test_special_i1e(shape, dtype, caplog):
27+
inp = torch.randn(shape, dtype=dtype, device=flag_gems.device)
28+
ref_inp = utils.to_reference(inp, True)
29+
ref_out = torch.special.i1e(ref_inp)
30+
with caplog.at_level("DEBUG", logger="flag_gems.ops.special_i1e"):
31+
with flag_gems.use_gems():
32+
res_out = torch.special.i1e(inp)
33+
assert "GEMS SPECIAL_I1E" in caplog.text
34+
utils.gems_assert_close(res_out, ref_out, dtype)

0 commit comments

Comments
 (0)