Skip to content

Commit 32b7119

Browse files
Add float_power operator
Signed-off-by: xuanzhengdu-eng <xuanzhengdu@gmail.com>
1 parent bfeca79 commit 32b7119

6 files changed

Lines changed: 465 additions & 2 deletions

File tree

benchmark/test_float_power.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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+
from . import base, consts
19+
20+
21+
class FloatPowerBenchmark(base.GenericBenchmark):
22+
def set_shapes(self, shape_file_path=None):
23+
self.shapes = [(4,), (1024,), (65536,), (512, 512), (2048, 2048)]
24+
25+
26+
def _tensor_tensor_input(shape, dtype, device):
27+
base_tensor = torch.rand(shape, dtype=dtype, device=device).add_(0.25)
28+
exponent = torch.rand(shape, dtype=dtype, device=device).mul_(4).sub_(2)
29+
yield base_tensor, exponent
30+
31+
32+
def _tensor_scalar_input(shape, dtype, device):
33+
base_tensor = torch.rand(shape, dtype=dtype, device=device).add_(0.25)
34+
yield base_tensor, 1.234
35+
36+
37+
def _scalar_tensor_input(shape, dtype, device):
38+
exponent = torch.rand(shape, dtype=dtype, device=device).mul_(4).sub_(2)
39+
yield 2.0, exponent
40+
41+
42+
def _tensor_tensor_out_input(shape, dtype, device):
43+
base_tensor = torch.rand(shape, dtype=dtype, device=device).add_(0.25)
44+
exponent = torch.rand(shape, dtype=dtype, device=device).mul_(4).sub_(2)
45+
out = torch.empty(shape, dtype=torch.float64, device=device)
46+
yield base_tensor, exponent, {"out": out}
47+
48+
49+
def _tensor_scalar_out_input(shape, dtype, device):
50+
base_tensor = torch.rand(shape, dtype=dtype, device=device).add_(0.25)
51+
out = torch.empty(shape, dtype=torch.float64, device=device)
52+
yield base_tensor, 1.234, {"out": out}
53+
54+
55+
def _scalar_tensor_out_input(shape, dtype, device):
56+
exponent = torch.rand(shape, dtype=dtype, device=device).mul_(4).sub_(2)
57+
out = torch.empty(shape, dtype=torch.float64, device=device)
58+
yield 2.0, exponent, {"out": out}
59+
60+
61+
def _run_benchmark(op_name, input_fn, torch_op):
62+
bench = FloatPowerBenchmark(
63+
input_fn=input_fn,
64+
op_name=op_name,
65+
torch_op=torch_op,
66+
dtypes=consts.FLOAT_DTYPES,
67+
)
68+
bench.run()
69+
70+
71+
@pytest.mark.float_power_tensor_tensor
72+
def test_float_power_tensor_tensor():
73+
_run_benchmark(
74+
"float_power_tensor_tensor",
75+
_tensor_tensor_input,
76+
torch.ops.aten.float_power.Tensor_Tensor,
77+
)
78+
79+
80+
@pytest.mark.float_power_tensor_scalar
81+
def test_float_power_tensor_scalar():
82+
_run_benchmark(
83+
"float_power_tensor_scalar",
84+
_tensor_scalar_input,
85+
torch.ops.aten.float_power.Tensor_Scalar,
86+
)
87+
88+
89+
@pytest.mark.float_power_scalar_tensor
90+
def test_float_power_scalar_tensor():
91+
_run_benchmark(
92+
"float_power_scalar_tensor",
93+
_scalar_tensor_input,
94+
torch.ops.aten.float_power.Scalar,
95+
)
96+
97+
98+
@pytest.mark.float_power_tensor_tensor_out
99+
def test_float_power_tensor_tensor_out():
100+
_run_benchmark(
101+
"float_power_tensor_tensor_out",
102+
_tensor_tensor_out_input,
103+
torch.ops.aten.float_power.Tensor_Tensor_out,
104+
)
105+
106+
107+
@pytest.mark.float_power_tensor_scalar_out
108+
def test_float_power_tensor_scalar_out():
109+
_run_benchmark(
110+
"float_power_tensor_scalar_out",
111+
_tensor_scalar_out_input,
112+
torch.ops.aten.float_power.Tensor_Scalar_out,
113+
)
114+
115+
116+
@pytest.mark.float_power_scalar_tensor_out
117+
def test_float_power_scalar_tensor_out():
118+
_run_benchmark(
119+
"float_power_scalar_tensor_out",
120+
_scalar_tensor_out_input,
121+
torch.ops.aten.float_power.Scalar_out,
122+
)

conf/operators.yaml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3542,6 +3542,72 @@ ops:
35423542
- Math
35433543
stages:
35443544
- alpha: '5.4'
3545+
- id: float_power_scalar_tensor
3546+
description: Computes a scalar base raised element-wise to tensor exponents in float64.
3547+
for:
3548+
- float_power.Scalar
3549+
labels:
3550+
- aten
3551+
- KernelGen
3552+
kind:
3553+
- Math
3554+
stages:
3555+
- alpha: '5.4'
3556+
- id: float_power_scalar_tensor_out
3557+
description: Out variant of float_power.Scalar.
3558+
for:
3559+
- float_power.Scalar_out
3560+
labels:
3561+
- aten
3562+
- KernelGen
3563+
kind:
3564+
- Math
3565+
stages:
3566+
- alpha: '5.4'
3567+
- id: float_power_tensor_scalar
3568+
description: Raises tensor elements to a scalar exponent in float64.
3569+
for:
3570+
- float_power.Tensor_Scalar
3571+
labels:
3572+
- aten
3573+
- KernelGen
3574+
kind:
3575+
- Math
3576+
stages:
3577+
- alpha: '5.4'
3578+
- id: float_power_tensor_scalar_out
3579+
description: Out variant of float_power.Tensor_Scalar.
3580+
for:
3581+
- float_power.Tensor_Scalar_out
3582+
labels:
3583+
- aten
3584+
- KernelGen
3585+
kind:
3586+
- Math
3587+
stages:
3588+
- alpha: '5.4'
3589+
- id: float_power_tensor_tensor
3590+
description: Raises tensor elements to tensor exponents in float64 with broadcasting.
3591+
for:
3592+
- float_power.Tensor_Tensor
3593+
labels:
3594+
- aten
3595+
- KernelGen
3596+
kind:
3597+
- Math
3598+
stages:
3599+
- alpha: '5.4'
3600+
- id: float_power_tensor_tensor_out
3601+
description: Out variant of float_power.Tensor_Tensor.
3602+
for:
3603+
- float_power.Tensor_Tensor_out
3604+
labels:
3605+
- aten
3606+
- KernelGen
3607+
kind:
3608+
- Math
3609+
stages:
3610+
- alpha: '5.4'
35453611
- id: floor
35463612
description: |
35473613
Performs an element-wise floor operation, rounding each element of a tensor

src/flag_gems/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,12 @@ def torch_ge(v):
478478
("fix_", fix_),
479479
("flatten.using_ints", flatten),
480480
("flip", flip),
481+
("float_power.Scalar", float_power_scalar_tensor),
482+
("float_power.Scalar_out", float_power_scalar_tensor_out),
483+
("float_power.Tensor_Scalar", float_power_tensor_scalar),
484+
("float_power.Tensor_Scalar_out", float_power_tensor_scalar_out),
485+
("float_power.Tensor_Tensor", float_power_tensor_tensor),
486+
("float_power.Tensor_Tensor_out", float_power_tensor_tensor_out),
481487
("float_power_.Scalar", float_power_tensor_scalar_),
482488
("float_power_.Tensor", float_power_tensor_tensor_),
483489
("floor", floor),

src/flag_gems/ops/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,14 @@
303303
from flag_gems.ops.flatten import flatten
304304
from flag_gems.ops.flip import flip
305305
from flag_gems.ops.float_power_ import (
306+
float_power_scalar_tensor,
307+
float_power_scalar_tensor_out,
306308
float_power_tensor_scalar,
307309
float_power_tensor_scalar_,
310+
float_power_tensor_scalar_out,
308311
float_power_tensor_tensor,
309312
float_power_tensor_tensor_,
313+
float_power_tensor_tensor_out,
310314
)
311315
from flag_gems.ops.floor import floor, floor_out
312316
from flag_gems.ops.floor_ import floor_
@@ -1117,10 +1121,14 @@
11171121
"flash_attn_varlen_func",
11181122
"flash_attn_varlen_opt_func",
11191123
"flip",
1124+
"float_power_scalar_tensor",
1125+
"float_power_scalar_tensor_out",
11201126
"float_power_tensor_scalar",
11211127
"float_power_tensor_scalar_",
1128+
"float_power_tensor_scalar_out",
11221129
"float_power_tensor_tensor",
11231130
"float_power_tensor_tensor_",
1131+
"float_power_tensor_tensor_out",
11241132
"floor",
11251133
"floor_",
11261134
"floor_divide",

src/flag_gems/ops/float_power_.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Generated by KernelGen: https://github.com/flagos-ai/KernelGen
22
import logging
33

4+
import torch
45
import triton
56
import triton.language as tl
67

@@ -18,7 +19,12 @@ def float_power_func(x, exponent):
1819

1920
def float_power_tensor_tensor(A, exponent):
2021
logger.debug("GEMS FLOAT_POWER_TENSOR_TENSOR")
21-
return float_power_func(A, exponent)
22+
out = torch.empty(
23+
torch.broadcast_shapes(A.shape, exponent.shape),
24+
dtype=torch.float64,
25+
device=A.device,
26+
)
27+
return float_power_tensor_tensor_func(A, exponent, out0=out)
2228

2329

2430
def float_power_tensor_tensor_(A, exponent):
@@ -34,9 +40,68 @@ def float_power_func_tensor_scalar(x, exponent):
3440

3541
def float_power_tensor_scalar(A, exponent):
3642
logger.debug("GEMS FLOAT_POWER_TENSOR_SCALAR")
37-
return float_power_func_tensor_scalar(A, exponent)
43+
out = torch.empty(A.shape, dtype=torch.float64, device=A.device)
44+
return float_power_tensor_scalar_func(A, exponent, out0=out)
3845

3946

4047
def float_power_tensor_scalar_(A, exponent):
4148
logger.debug("GEMS FLOAT_POWER_TENSOR_SCALAR_")
4249
return float_power_func_tensor_scalar(A, exponent, out0=A)
50+
51+
52+
@pointwise_dynamic(promotion_methods=[(0, 1, "DEFAULT")])
53+
@triton.jit
54+
def float_power_tensor_tensor_func(x, exponent):
55+
return _pow(x.to(tl.float64), exponent.to(tl.float64))
56+
57+
58+
@pointwise_dynamic(is_tensor=[True, False], promotion_methods=[(0, 1, "DEFAULT")])
59+
@triton.jit
60+
def float_power_tensor_scalar_func(x, exponent):
61+
return _pow(x.to(tl.float64), exponent.to(tl.float64))
62+
63+
64+
@pointwise_dynamic(is_tensor=[False, True], promotion_methods=[(0, 1, "DEFAULT")])
65+
@triton.jit
66+
def float_power_scalar_tensor_func(x, exponent):
67+
return _pow(x.to(tl.float64), exponent.to(tl.float64))
68+
69+
70+
def _prepare_out(out, shape, device):
71+
if out.dtype != torch.float64:
72+
raise RuntimeError(
73+
f"the output given to float_power has dtype {out.dtype} "
74+
"but the operation's result requires dtype Double"
75+
)
76+
if out.device != device:
77+
raise RuntimeError(
78+
f"Expected out tensor to have device {device}, but got {out.device} instead"
79+
)
80+
if out.shape != shape:
81+
out.resize_(shape)
82+
return out
83+
84+
85+
def float_power_tensor_tensor_out(A, exponent, *, out):
86+
logger.debug("GEMS FLOAT_POWER_TENSOR_TENSOR_OUT")
87+
shape = torch.broadcast_shapes(A.shape, exponent.shape)
88+
_prepare_out(out, shape, A.device)
89+
return float_power_tensor_tensor_func(A, exponent, out0=out)
90+
91+
92+
def float_power_tensor_scalar_out(A, exponent, *, out):
93+
logger.debug("GEMS FLOAT_POWER_TENSOR_SCALAR_OUT")
94+
_prepare_out(out, A.shape, A.device)
95+
return float_power_tensor_scalar_func(A, exponent, out0=out)
96+
97+
98+
def float_power_scalar_tensor(A, exponent):
99+
logger.debug("GEMS FLOAT_POWER_SCALAR_TENSOR")
100+
out = torch.empty(exponent.shape, dtype=torch.float64, device=exponent.device)
101+
return float_power_scalar_tensor_func(A, exponent, out0=out)
102+
103+
104+
def float_power_scalar_tensor_out(A, exponent, *, out):
105+
logger.debug("GEMS FLOAT_POWER_SCALAR_TENSOR_OUT")
106+
_prepare_out(out, exponent.shape, exponent.device)
107+
return float_power_scalar_tensor_func(A, exponent, out0=out)

0 commit comments

Comments
 (0)