Skip to content

Commit 84c1a96

Browse files
authored
Merge branch 'master' into pr/mthreads-reflection_pad3d_backward
2 parents 4f55a45 + 43ee940 commit 84c1a96

18 files changed

Lines changed: 2890 additions & 0 deletions

src/flag_gems/runtime/backend/_iluvatar/ops/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
from .mm import mm, mm_out
2424
from .repeat import repeat
2525
from .scatter_add import scatter_add_
26+
from .special_chebyshev_polynomial_w import (
27+
special_chebyshev_polynomial_w,
28+
special_chebyshev_polynomial_w_out,
29+
)
30+
from .special_gammainc import special_gammainc
2631
from .tile import tile
2732
from .var import var, var_correction, var_dim
2833

@@ -39,6 +44,9 @@
3944
"matmul_int8",
4045
"repeat",
4146
"scatter_add_",
47+
"special_chebyshev_polynomial_w",
48+
"special_chebyshev_polynomial_w_out",
49+
"special_gammainc",
4250
"tile",
4351
"var",
4452
"var_correction",
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
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 chebyshev_polynomial_w_scalar_n_kernel(
29+
x_ptr,
30+
n_ptr,
31+
out_ptr,
32+
n_elements,
33+
BLOCK_SIZE: tl.constexpr,
34+
MAX_DEGREE: tl.constexpr,
35+
):
36+
"""Optimized kernel for scalar n.
37+
38+
Reads n from device memory (no host-device sync needed).
39+
Uses fixed MAX_DEGREE unrolling with tl.where to select result.
40+
"""
41+
pid = tl.program_id(axis=0)
42+
block_start = pid * BLOCK_SIZE
43+
offsets = block_start + tl.arange(0, BLOCK_SIZE)
44+
mask = offsets < n_elements
45+
46+
x = tl.load(x_ptr + offsets, mask=mask, other=0.0)
47+
# Load scalar n (single element) - avoids host-device sync
48+
n_val = tl.load(n_ptr).to(tl.int32)
49+
50+
x_f32 = x.to(tl.float32)
51+
52+
# W_0(x) = 1
53+
w0 = 1.0 + 0.0 * x_f32
54+
# W_1(x) = 2x + 1
55+
w1 = 2.0 * x_f32 + 1.0
56+
57+
result = w0
58+
result = tl.where(n_val >= 1, w1, result)
59+
60+
# Recurrence: W_k(x) = 2*x*W_{k-1}(x) - W_{k-2}(x)
61+
w_km2 = w0
62+
w_km1 = w1
63+
for k in tl.static_range(2, MAX_DEGREE):
64+
w_k = 2.0 * x_f32 * w_km1 - w_km2
65+
result = tl.where(n_val >= k, w_k, result)
66+
w_km2 = w_km1
67+
w_km1 = w_k
68+
69+
tl.store(out_ptr + offsets, result, mask=mask)
70+
71+
72+
@triton.jit
73+
def chebyshev_polynomial_w_kernel(
74+
x_ptr,
75+
n_ptr,
76+
out_ptr,
77+
n_elements,
78+
BLOCK_SIZE: tl.constexpr,
79+
MAX_DEGREE: tl.constexpr,
80+
):
81+
"""General kernel when n varies per element."""
82+
pid = tl.program_id(axis=0)
83+
block_start = pid * BLOCK_SIZE
84+
offsets = block_start + tl.arange(0, BLOCK_SIZE)
85+
mask = offsets < n_elements
86+
87+
x = tl.load(x_ptr + offsets, mask=mask, other=0.0)
88+
n = tl.load(n_ptr + offsets, mask=mask, other=0)
89+
90+
x_f32 = x.to(tl.float32)
91+
n_i32 = n.to(tl.int32)
92+
93+
# W_0(x) = 1
94+
w0 = 1.0 + 0.0 * x_f32
95+
# W_1(x) = 2x + 1
96+
w1 = 2.0 * x_f32 + 1.0
97+
98+
result = w0
99+
result = tl.where(n_i32 >= 1, w1, result)
100+
101+
# Compute W_k for k >= 2 using recurrence
102+
w_km2 = w0
103+
w_km1 = w1
104+
for k in tl.static_range(2, MAX_DEGREE):
105+
w_k = 2.0 * x_f32 * w_km1 - w_km2
106+
result = tl.where(n_i32 >= k, w_k, result)
107+
w_km2 = w_km1
108+
w_km1 = w_k
109+
110+
tl.store(out_ptr + offsets, result, mask=mask)
111+
112+
113+
# Fixed unroll depth: 21 covers most practical polynomial degrees.
114+
# Avoids host-device sync to read n value at runtime.
115+
_DEFAULT_MAX_DEGREE = 21
116+
117+
118+
def _launch_chebyshev_w(out: torch.Tensor, x: torch.Tensor, n: torch.Tensor):
119+
assert (
120+
x.device.type == flag_gems.device
121+
and n.device.type == flag_gems.device
122+
and out.device.type == flag_gems.device
123+
), f"All tensors must be {flag_gems.device} tensors"
124+
125+
x_in = x
126+
if not x_in.is_floating_point():
127+
x_in = x_in.to(torch.get_default_dtype())
128+
if x_in.dtype != out.dtype:
129+
x_in = x_in.to(out.dtype)
130+
131+
x_contig = x_in.contiguous()
132+
out_was_noncontig = not out.is_contiguous()
133+
out_contig = out.contiguous() if out_was_noncontig else out
134+
n_elements = out_contig.numel()
135+
BLOCK_SIZE = 1024
136+
grid = lambda meta: (triton.cdiv(n_elements, meta["BLOCK_SIZE"]),)
137+
138+
if n.dim() == 0 or n.numel() == 1:
139+
# Scalar n: read n in kernel from device memory, no sync.
140+
n_contig = n.contiguous()
141+
chebyshev_polynomial_w_scalar_n_kernel[grid](
142+
x_contig,
143+
n_contig,
144+
out_contig,
145+
n_elements,
146+
BLOCK_SIZE=BLOCK_SIZE,
147+
MAX_DEGREE=_DEFAULT_MAX_DEGREE,
148+
num_warps=4,
149+
num_stages=2,
150+
)
151+
else:
152+
# General case: n varies per element, need broadcast
153+
x_in_b, n_in_b = torch.broadcast_tensors(x_in, n)
154+
x_contig = x_in_b.contiguous()
155+
n_contig = n_in_b.contiguous()
156+
157+
chebyshev_polynomial_w_kernel[grid](
158+
x_contig,
159+
n_contig,
160+
out_contig,
161+
n_elements,
162+
BLOCK_SIZE=BLOCK_SIZE,
163+
MAX_DEGREE=_DEFAULT_MAX_DEGREE,
164+
num_warps=4,
165+
num_stages=2,
166+
)
167+
168+
if out_was_noncontig:
169+
out.copy_(out_contig)
170+
return out
171+
172+
173+
def special_chebyshev_polynomial_w(x, n):
174+
logger.debug("GEMS_ILUVATAR SPECIAL_CHEBYSHEV_POLYNOMIAL_W")
175+
if not isinstance(x, torch.Tensor):
176+
x = torch.tensor(x, dtype=torch.float32)
177+
if x.device.type != flag_gems.device:
178+
raise ValueError(
179+
"special_chebyshev_polynomial_w: "
180+
f"input x must be on {flag_gems.device} device"
181+
)
182+
if x.dtype not in (torch.float32, torch.float64):
183+
raise ValueError(
184+
"special_chebyshev_polynomial_w only supports "
185+
f"float32/float64, got {x.dtype}"
186+
)
187+
if not isinstance(n, torch.Tensor):
188+
n = torch.tensor(n, dtype=torch.int64, device=x.device)
189+
if n.device.type != flag_gems.device:
190+
n = n.to(x.device)
191+
192+
out = torch.empty_like(x)
193+
_launch_chebyshev_w(out, x, n)
194+
return out
195+
196+
197+
def special_chebyshev_polynomial_w_out(x, n, out):
198+
logger.debug("GEMS_ILUVATAR SPECIAL_CHEBYSHEV_POLYNOMIAL_W_OUT")
199+
if not isinstance(x, torch.Tensor):
200+
x = torch.tensor(x, dtype=torch.float32)
201+
if x.device.type != flag_gems.device:
202+
raise ValueError(
203+
"special_chebyshev_polynomial_w: "
204+
f"input x must be on {flag_gems.device} device"
205+
)
206+
if x.dtype not in (torch.float32, torch.float64):
207+
raise ValueError(
208+
"special_chebyshev_polynomial_w only supports "
209+
f"float32/float64, got {x.dtype}"
210+
)
211+
if not isinstance(n, torch.Tensor):
212+
n = torch.tensor(n, dtype=torch.int64, device=x.device)
213+
if n.device.type != flag_gems.device:
214+
n = n.to(x.device)
215+
216+
_launch_chebyshev_w(out, x, n)
217+
return out

0 commit comments

Comments
 (0)