Files
ray-project--ray/python/ray/tests/test_channel_serialization.py
T
2026-07-13 13:17:40 +08:00

158 lines
5.5 KiB
Python

# coding: utf-8
import logging
import os
import sys
import pytest
import torch
from ray.experimental.channel.serialization_context import _SerializationContext
from ray.experimental.util.types import Device
logger = logging.getLogger(__name__)
@pytest.mark.parametrize(
"scalar_and_dtype",
[
# Basic tests
(1.23456, torch.float16),
(1.23456, torch.bfloat16),
(1.23456, torch.float32),
(1.23456, torch.float64),
(123, torch.int8),
(123, torch.int16),
(123456, torch.int32),
(123456, torch.int64),
(123, torch.uint8),
(123, torch.uint16),
(123456, torch.uint32),
(123456, torch.uint64),
(True, torch.bool),
# Boundary values tests - integers
(127, torch.int8), # INT8_MAX
(-128, torch.int8), # INT8_MIN
(32767, torch.int16), # INT16_MAX
(-32768, torch.int16), # INT16_MIN
(2147483647, torch.int32), # INT32_MAX
(-2147483648, torch.int32), # INT32_MIN
(9223372036854775807, torch.int64), # INT64_MAX
(-9223372036854775808, torch.int64), # INT64_MIN
# Boundary values tests - unsigned integers
(255, torch.uint8), # UINT8_MAX
(0, torch.uint8), # UINT8_MIN
(65535, torch.uint16), # UINT16_MAX
(0, torch.uint16), # UINT16_MIN
(4294967295, torch.uint32), # UINT32_MAX
(0, torch.uint32), # UINT32_MIN
(18446744073709551615, torch.uint64), # UINT64_MAX
(0, torch.uint64), # UINT64_MIN
# Floating point special values
(float("inf"), torch.float32),
(float("-inf"), torch.float32),
(float("nan"), torch.float32),
(float("inf"), torch.float64),
(float("-inf"), torch.float64),
(float("nan"), torch.float64),
# Float precision tests
(1.2345678901234567, torch.float32), # Beyond float32 precision
(1.2345678901234567, torch.float64), # Within float64 precision
(1e-45, torch.float32), # Near float32 smallest positive normal
(
2.2250738585072014e-308,
torch.float64,
), # Near float64 smallest positive normal
],
)
def test_scalar_tensor(scalar_and_dtype):
scalar, dtype = scalar_and_dtype
context = _SerializationContext()
scalar_tensor = torch.tensor(scalar, dtype=dtype)
np_array, tensor_dtype, tensor_device_type = context.serialize_to_numpy_or_scalar(
scalar_tensor
)
assert tensor_dtype == dtype
deserialized_tensor = context.deserialize_from_numpy_or_scalar(
np_array, dtype, tensor_device_type, Device.CPU
)
# Special handling for NaN values
if torch.is_floating_point(scalar_tensor) and torch.isnan(scalar_tensor):
assert torch.isnan(deserialized_tensor)
else:
assert (deserialized_tensor == scalar_tensor).all()
@pytest.mark.parametrize(
"tensor_shape_and_dtype",
[
((10, 10), torch.float16),
((10, 10), torch.bfloat16),
((10, 10, 10), torch.float32),
((10, 10, 10, 10), torch.float64),
((10, 10), torch.int8),
((10, 10), torch.int16),
((10, 10), torch.int32),
((10, 10), torch.int64),
((10, 10), torch.uint8),
((10, 10), torch.uint16),
((10, 10), torch.uint32),
((10, 10), torch.uint64),
],
)
def test_non_scalar_tensor(tensor_shape_and_dtype):
tensor_shape, dtype = tensor_shape_and_dtype
context = _SerializationContext()
# Create tensor based on dtype with varying values
if dtype in [torch.float16, torch.bfloat16, torch.float32, torch.float64]:
# For floating point types, use randn
tensor = torch.randn(*tensor_shape).to(dtype)
else:
# For integer types, create varying values within appropriate ranges
total_elements = torch.prod(torch.tensor(tensor_shape)).item()
if dtype == torch.uint8:
# Range: 0 to 255
values = torch.arange(0, min(total_elements, 256), dtype=torch.int32) % 256
elif dtype == torch.uint16:
# Range: 0 to 65535
values = (
torch.arange(0, min(total_elements, 65536), dtype=torch.int32) % 65536
)
elif dtype == torch.int8:
# Range: -128 to 127
values = (
torch.arange(0, min(total_elements, 256), dtype=torch.int32) % 256
) - 128
elif dtype == torch.int16:
# Range: -32768 to 32767
values = (
torch.arange(0, min(total_elements, 65536), dtype=torch.int32) % 65536
) - 32768
elif dtype == torch.int32:
# Use a smaller range to avoid overflow
values = torch.arange(0, total_elements, dtype=torch.int32) % 10000 - 5000
else: # int64
# Use a smaller range to avoid overflow
values = torch.arange(0, total_elements, dtype=torch.int64) % 10000 - 5000
# Reshape the values to match the target shape
tensor = values.reshape(tensor_shape).to(dtype)
np_array, tensor_dtype, tensor_device_type = context.serialize_to_numpy_or_scalar(
tensor
)
deserialized_tensor = context.deserialize_from_numpy_or_scalar(
np_array, tensor_dtype, tensor_device_type, Device.CPU
)
assert (tensor == deserialized_tensor).all()
if __name__ == "__main__":
if os.environ.get("PARALLEL_CI"):
sys.exit(pytest.main(["-n", "auto", "--boxed", "-vs", __file__]))
else:
sys.exit(pytest.main(["-sv", __file__]))