Skip to content

Commit bf52906

Browse files
authored
Split the special ops test cases (#2600)
1 parent 5c7fe9f commit bf52906

54 files changed

Lines changed: 3393 additions & 2921 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

tests/test_apply_rotary_pos_emb.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import random
2+
import time
3+
from typing import Optional
4+
5+
import pytest
6+
import torch
7+
8+
import flag_gems
9+
10+
from . import accuracy_utils as utils
11+
from . import conftest as cfg
12+
13+
random.seed(time.time() // 100)
14+
15+
16+
# Copied from transformers.models.llama.modeling_llama.rotate_half
17+
# https://github.com/huggingface/transformers/blob/main/src/transformers/models/llama/modeling_llama.py
18+
def rotate_half(x):
19+
"""Rotates half the hidden dims of the input."""
20+
x1 = x[..., : x.shape[-1] // 2]
21+
x2 = x[..., x.shape[-1] // 2 :]
22+
return torch.cat((-x2, x1), dim=-1)
23+
24+
25+
# Copied from transformers.models.cohere.modeling_cohere.rotate_half
26+
# https://github.com/huggingface/transformers/blob/main/src/transformers/models/cohere/modeling_cohere.py
27+
def rotate_interleave(x):
28+
"""Rotates interleave the hidden dims of the input."""
29+
x1 = x[..., ::2]
30+
x2 = x[..., 1::2]
31+
return torch.stack((-x2, x1), dim=-1).flatten(-2)
32+
33+
34+
def _torch_apply_rotary_pos_emb(
35+
q,
36+
k,
37+
cos,
38+
sin,
39+
position_ids: Optional[torch.Tensor] = None,
40+
rotary_interleaved: bool = False,
41+
):
42+
q = q.float()
43+
k = k.float()
44+
if position_ids is None:
45+
cos = cos[None, : q.size(-3), None, :]
46+
sin = sin[None, : q.size(-3), None, :]
47+
else:
48+
cos = cos[position_ids].unsqueeze(-2) # [bs, seq_len, 1, dim/2]
49+
sin = sin[position_ids].unsqueeze(-2) # [bs, seq_len, 1, dim/2]
50+
if rotary_interleaved:
51+
cos = torch.repeat_interleave(cos, 2, dim=-1) # [bs, seq_len, 1, dim]
52+
sin = torch.repeat_interleave(sin, 2, dim=-1) # [bs, seq_len, 1, dim]
53+
rotate_fn = rotate_interleave
54+
else:
55+
cos = torch.cat([cos, cos], dim=-1) # [bs, seq_len, 1, dim]
56+
sin = torch.cat([sin, sin], dim=-1) # [bs, seq_len, 1, dim]
57+
rotate_fn = rotate_half
58+
59+
q_embed = (q * cos) + (rotate_fn(q) * sin)
60+
k_embed = (k * cos) + (rotate_fn(k) * sin)
61+
62+
return q_embed, k_embed
63+
64+
65+
def _get_rope_cos_sin(max_seq_len, dim, dtype, base=10000, device=flag_gems.device):
66+
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float().to(device) / dim))
67+
t = torch.arange(max_seq_len, device=device, dtype=inv_freq.dtype)
68+
freqs = torch.outer(t, inv_freq)
69+
cos = freqs.cos().to(dtype)
70+
sin = freqs.sin().to(dtype)
71+
72+
return cos, sin
73+
74+
75+
@pytest.mark.apply_rotary_pos_emb
76+
@pytest.mark.parametrize("batch_size", [2] if cfg.TO_CPU else [4, 8])
77+
@pytest.mark.parametrize("max_seq_len", [16] if cfg.TO_CPU else [512, 2048])
78+
@pytest.mark.parametrize("q_heads,k_heads", [(8, 1), (6, 2), (1, 1), (8, 8)])
79+
@pytest.mark.parametrize("head_dim", [8] if cfg.TO_CPU else [64, 96, 128, 256])
80+
@pytest.mark.parametrize("dtype", utils.FLOAT_DTYPES)
81+
@pytest.mark.parametrize("rotary_interleaved", [True, False])
82+
@pytest.mark.parametrize("has_pos_id", [True, False])
83+
def test_apply_rotary_pos_emb(
84+
batch_size,
85+
max_seq_len,
86+
q_heads,
87+
k_heads,
88+
head_dim,
89+
dtype,
90+
has_pos_id,
91+
rotary_interleaved,
92+
):
93+
seq_len = torch.randint(1, max_seq_len, (1,)).item()
94+
q = torch.randn(
95+
(batch_size, seq_len, q_heads, head_dim), dtype=dtype, device=flag_gems.device
96+
)
97+
k = torch.randn(
98+
(batch_size, seq_len, k_heads, head_dim), dtype=dtype, device=flag_gems.device
99+
)
100+
101+
position_ids = torch.randint(
102+
0, max_seq_len, (batch_size, seq_len), device=flag_gems.device
103+
)
104+
cos, sin = _get_rope_cos_sin(max_seq_len, head_dim, dtype, device=flag_gems.device)
105+
106+
ref_q = utils.to_reference(q, True)
107+
ref_k = utils.to_reference(k, True)
108+
ref_cos = utils.to_reference(cos, True)
109+
ref_sin = utils.to_reference(sin, True)
110+
ref_position_ids = utils.to_reference(position_ids)
111+
112+
q_embed_ref, k_embed_ref = _torch_apply_rotary_pos_emb(
113+
q=ref_q,
114+
k=ref_k,
115+
cos=ref_cos,
116+
sin=ref_sin,
117+
position_ids=ref_position_ids if has_pos_id else None,
118+
rotary_interleaved=rotary_interleaved,
119+
)
120+
q_embed_out, k_embed_out = flag_gems.apply_rotary_pos_emb(
121+
q=q,
122+
k=k,
123+
cos=cos,
124+
sin=sin,
125+
position_ids=position_ids if has_pos_id else None,
126+
rotary_interleaved=rotary_interleaved,
127+
)
128+
129+
utils.gems_assert_close(q_embed_out, q_embed_ref, dtype)
130+
utils.gems_assert_close(k_embed_out, k_embed_ref, dtype)

tests/test_arange.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import pytest
2+
import torch
3+
4+
import flag_gems
5+
6+
from . import accuracy_utils as utils
7+
from . import conftest as cfg
8+
9+
10+
@pytest.mark.arange
11+
@pytest.mark.parametrize("start", utils.ARANGE_START)
12+
@pytest.mark.parametrize("step", [1, 2, 5])
13+
@pytest.mark.parametrize("end", [128, 256, 1024])
14+
@pytest.mark.parametrize("dtype", utils.FLOAT_DTYPES + utils.ALL_INT_DTYPES + [None])
15+
@pytest.mark.parametrize("device", [flag_gems.device, None])
16+
@pytest.mark.parametrize(
17+
"pin_memory", [False, None]
18+
) # Since triton only target to GPU, pin_memory only used in CPU tensors.
19+
def test_arange(start, step, end, dtype, device, pin_memory):
20+
ref_out = torch.arange(
21+
start,
22+
end,
23+
step,
24+
dtype=dtype,
25+
device="cpu" if cfg.TO_CPU else device,
26+
pin_memory=pin_memory,
27+
)
28+
with flag_gems.use_gems():
29+
res_out = torch.arange(
30+
start, end, step, dtype=dtype, device=device, pin_memory=pin_memory
31+
)
32+
33+
utils.gems_assert_equal(res_out, ref_out)

tests/test_assert_async.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import pytest
2+
import torch
3+
4+
import flag_gems
5+
6+
7+
@pytest.mark.assert_async
8+
@pytest.mark.parametrize(
9+
"shape, value, expected_err, match_str",
10+
[
11+
((), 1.0, None, None),
12+
((2,), 1.0, RuntimeError, "is ambiguous"),
13+
((1,), 1.0, None, None),
14+
],
15+
)
16+
def test_assert_async(shape, value, expected_err, match_str):
17+
msg = "Assertion failed!"
18+
inp_pt = torch.full(shape, value, device=flag_gems.device)
19+
inp_triton = inp_pt.clone()
20+
if expected_err:
21+
with flag_gems.use_gems():
22+
with pytest.raises(expected_err, match=match_str):
23+
flag_gems._assert_async(inp_triton, msg)
24+
if value == 0:
25+
torch.cuda.synchronize()
26+
else:
27+
with flag_gems.use_gems():
28+
flag_gems._assert_async(inp_triton, msg)
29+
torch.cuda.synchronize()
30+
31+
if expected_err:
32+
with pytest.raises(expected_err, match=match_str):
33+
torch._assert_async(inp_pt, msg)
34+
if value == 0:
35+
torch.cuda.synchronize()
36+
else:
37+
torch._assert_async(inp_pt, msg)
38+
torch.cuda.synchronize()

tests/test_cat.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import pytest
2+
import torch
3+
4+
import flag_gems
5+
6+
from . import accuracy_utils as utils
7+
8+
CAT_SHAPES = [
9+
[(1, 32), (8, 32)],
10+
[(16, 128), (32, 128)],
11+
[(1024, 1024), (1024, 1024)],
12+
[(1, 1024, 256), (8, 1024, 256), (16, 1024, 256)],
13+
[(16, 320, 15), (32, 320, 15), (64, 320, 15)],
14+
[(16, 128, 64, 64), (16, 128, 64, 64), (24, 128, 64, 64), (32, 128, 64, 64)],
15+
]
16+
17+
18+
def gen_cat_shapes_dim(shapes):
19+
results = []
20+
for tensor_shapes in shapes:
21+
assert all(
22+
[len(s) == len(tensor_shapes[0]) for s in tensor_shapes]
23+
), "All tensor rank must agree."
24+
25+
assert all(
26+
[s[-1] == tensor_shapes[0][-1] for s in tensor_shapes]
27+
), "All tensor must have same shape except cat dim."
28+
29+
rank = len(tensor_shapes[0])
30+
results.append([tensor_shapes, 0])
31+
for dim in range(1, rank):
32+
results.append(
33+
[[(s[dim], *s[1:dim], s[0], *s[dim + 1 :]) for s in tensor_shapes], dim]
34+
)
35+
results.append(
36+
[
37+
[(s[dim], *s[1:dim], s[0], *s[dim + 1 :]) for s in tensor_shapes],
38+
dim - rank,
39+
]
40+
)
41+
return results
42+
43+
44+
@pytest.mark.cat
45+
@pytest.mark.parametrize("shape, dim", gen_cat_shapes_dim(CAT_SHAPES))
46+
@pytest.mark.parametrize("dtype", utils.FLOAT_DTYPES + utils.INT_DTYPES)
47+
def test_cat(shape, dim, dtype):
48+
if dtype in utils.FLOAT_DTYPES:
49+
inp = [torch.randn(s, dtype=dtype, device=flag_gems.device) for s in shape]
50+
else:
51+
inp = [
52+
torch.randint(low=0, high=0x7FFF, size=s, dtype=dtype, device="cpu").to(
53+
flag_gems.device
54+
)
55+
for s in shape
56+
]
57+
ref_inp = [utils.to_reference(_) for _ in inp]
58+
ref_out = torch.cat(ref_inp, dim)
59+
60+
with flag_gems.use_gems():
61+
res_out = torch.cat(inp, dim)
62+
utils.gems_assert_equal(res_out, ref_out)
63+
64+
65+
@pytest.mark.cat
66+
@pytest.mark.parametrize(
67+
"shape, dim",
68+
[
69+
(((0, 3), (2, 3)), 0),
70+
(((0, 3), (0, 3)), 0),
71+
(((0,), (0,)), 0),
72+
(((0,), (1, 3)), -1),
73+
(((0,), (1, 2, 3)), -2),
74+
(((0,), (1, 1, 2, 3)), -3),
75+
],
76+
)
77+
@pytest.mark.parametrize("dtype", [torch.float32])
78+
def test_accuracy_cat_empty_tensor(shape, dim, dtype):
79+
inp = [torch.randn(s, dtype=dtype, device=flag_gems.device) for s in shape]
80+
ref_inp = [utils.to_reference(_) for _ in inp]
81+
ref_out = torch.cat(ref_inp, dim)
82+
83+
with flag_gems.use_gems():
84+
res_out = torch.cat(inp, dim)
85+
86+
utils.gems_assert_equal(res_out, ref_out)

tests/test_conj_physical.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import pytest
2+
import torch
3+
4+
import flag_gems
5+
6+
from . import accuracy_utils as utils
7+
8+
9+
@pytest.mark.conj_physical
10+
@pytest.mark.parametrize("shape", [(256,), (32, 64), (2, 3, 4)])
11+
@pytest.mark.parametrize("is_complex", [True, False])
12+
@pytest.mark.parametrize("dtype", [torch.float16, torch.float32, torch.bfloat16])
13+
def test_conj_physical(shape, is_complex, dtype):
14+
device = flag_gems.device
15+
16+
if is_complex:
17+
real = torch.randn(shape, dtype=torch.float32, device=device)
18+
imag = torch.randn(shape, dtype=torch.float32, device=device)
19+
input = torch.complex(real, imag)
20+
out_dtype = input.dtype
21+
else:
22+
input = torch.randn(shape, dtype=dtype, device=device)
23+
out_dtype = dtype
24+
25+
ref_input = utils.to_reference(input, True)
26+
ref_out = torch.conj_physical(ref_input)
27+
with flag_gems.use_gems():
28+
res_out = torch.conj_physical(input)
29+
30+
utils.gems_assert_close(res_out, ref_out, out_dtype, reduce_dim=1)

tests/test_contiguous.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import pytest
2+
import torch
3+
4+
import flag_gems
5+
6+
from . import accuracy_utils as utils
7+
8+
9+
@pytest.mark.contiguous
10+
@pytest.mark.parametrize("shape", utils.SPECIAL_SHAPES)
11+
@pytest.mark.parametrize("dtype", utils.FLOAT_DTYPES + utils.ALL_INT_DTYPES)
12+
def test_accuracy_contiguous(shape, dtype):
13+
if shape[0] <= 2:
14+
return
15+
16+
if dtype in utils.FLOAT_DTYPES:
17+
inp = torch.randn(shape, dtype=dtype, device=flag_gems.device)
18+
else:
19+
inp = torch.randint(
20+
low=-10000, high=10000, size=shape, dtype=dtype, device="cpu"
21+
).to(flag_gems.device)
22+
23+
inp = inp[::2]
24+
assert inp.is_contiguous() is False
25+
26+
ref_inp = utils.to_reference(inp)
27+
ref_out = ref_inp.contiguous()
28+
with flag_gems.use_gems():
29+
res_out = inp.contiguous()
30+
31+
assert res_out.is_contiguous() is True
32+
assert res_out.is_contiguous() is True
33+
assert res_out.stride() == ref_out.stride()
34+
35+
utils.gems_assert_equal(res_out, ref_out)

tests/test_diag.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import pytest
2+
import torch
3+
4+
import flag_gems
5+
6+
from . import accuracy_utils as utils
7+
8+
9+
@pytest.mark.diag
10+
@pytest.mark.parametrize("shape", utils.UT_SHAPES_1D + utils.UT_SHAPES_2D)
11+
@pytest.mark.parametrize("diagonal", [-2, -1, 0, 1, 2])
12+
@pytest.mark.parametrize(
13+
"dtype", utils.FLOAT_DTYPES + utils.INT_DTYPES + utils.BOOL_TYPES
14+
)
15+
def test_diag(shape, diagonal, dtype):
16+
if flag_gems.vendor_name == "kunlunxin":
17+
torch.manual_seed(0)
18+
torch.cuda.manual_seed_all(0)
19+
20+
if dtype in utils.FLOAT_DTYPES:
21+
inp = torch.randn(shape, dtype=dtype, device=flag_gems.device)
22+
elif dtype in utils.BOOL_TYPES:
23+
inp = torch.randint(0, 2, size=shape, dtype=dtype, device="cpu").to(
24+
flag_gems.device
25+
)
26+
else:
27+
inp = torch.randint(0, 0x7FFF, size=shape, dtype=dtype, device="cpu").to(
28+
flag_gems.device
29+
)
30+
ref_inp = utils.to_reference(inp)
31+
32+
ref_out = torch.diag(ref_inp, diagonal)
33+
with flag_gems.use_gems():
34+
res_out = torch.diag(inp, diagonal)
35+
36+
utils.gems_assert_equal(res_out, ref_out)

0 commit comments

Comments
 (0)