Adds unit tests covering tensor operations, type enums, client functionality, and serialization for all 12 SDK implementations: - JavaScript (Vitest): tensor, types, client tests - Python (pytest): tensor, types, client tests - Go: standard library tests with httptest - Flutter (flutter_test): tensor, types tests - Java (JUnit 5): tensor, types tests - Rust: embedded module tests - Ruby (minitest): tensor, types tests - C# (xUnit): tensor, types tests Tests cover: - Tensor creation (zeros, ones, random, randn, eye, arange, linspace) - Tensor operations (reshape, transpose, indexing) - Reductions (sum, mean, std, min, max) - Activations (relu, sigmoid, softmax) - Serialization/deserialization - Type enums and configuration - Client request building - Error handling
239 lines
7.4 KiB
Python
239 lines
7.4 KiB
Python
"""Unit tests for Tensor class."""
|
|
|
|
import pytest
|
|
import numpy as np
|
|
from synor_compute import Tensor
|
|
from synor_compute.types import Precision
|
|
|
|
|
|
class TestTensorCreation:
|
|
"""Tests for tensor creation methods."""
|
|
|
|
def test_from_numpy_array(self):
|
|
"""Should create tensor from numpy array."""
|
|
arr = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32)
|
|
tensor = Tensor.from_numpy(arr)
|
|
|
|
assert tensor.shape == (2, 3)
|
|
assert tensor.size == 6
|
|
assert tensor.ndim == 2
|
|
assert tensor.dtype == Precision.FP32
|
|
|
|
def test_from_numpy_with_dtype(self):
|
|
"""Should create tensor with specified dtype."""
|
|
arr = np.array([1, 2, 3])
|
|
tensor = Tensor.from_numpy(arr, dtype=Precision.FP16)
|
|
|
|
assert tensor.dtype == Precision.FP16
|
|
|
|
def test_infer_dtype_fp64(self):
|
|
"""Should infer FP64 from float64 array."""
|
|
arr = np.array([1.0, 2.0], dtype=np.float64)
|
|
tensor = Tensor.from_numpy(arr)
|
|
|
|
assert tensor.dtype == Precision.FP64
|
|
|
|
def test_infer_dtype_int8(self):
|
|
"""Should infer INT8 from int8 array."""
|
|
arr = np.array([1, 2, 3], dtype=np.int8)
|
|
tensor = Tensor.from_numpy(arr)
|
|
|
|
assert tensor.dtype == Precision.INT8
|
|
|
|
|
|
class TestTensorFactoryMethods:
|
|
"""Tests for tensor factory methods."""
|
|
|
|
def test_zeros(self):
|
|
"""Should create tensor of zeros."""
|
|
tensor = Tensor.zeros((2, 3))
|
|
|
|
assert tensor.shape == (2, 3)
|
|
assert np.allclose(tensor.data, 0)
|
|
|
|
def test_zeros_with_dtype(self):
|
|
"""Should create zeros tensor with specified dtype."""
|
|
tensor = Tensor.zeros((2, 2), dtype=Precision.FP64)
|
|
|
|
assert tensor.dtype == Precision.FP64
|
|
assert tensor.data.dtype == np.float64
|
|
|
|
def test_ones(self):
|
|
"""Should create tensor of ones."""
|
|
tensor = Tensor.ones((3, 2))
|
|
|
|
assert tensor.shape == (3, 2)
|
|
assert np.allclose(tensor.data, 1)
|
|
|
|
def test_random(self):
|
|
"""Should create tensor with random values in [0, 1)."""
|
|
tensor = Tensor.random((10, 10))
|
|
|
|
assert tensor.shape == (10, 10)
|
|
assert tensor.data.min() >= 0
|
|
assert tensor.data.max() < 1
|
|
|
|
def test_randn(self):
|
|
"""Should create tensor with normal distribution."""
|
|
tensor = Tensor.randn((1000,))
|
|
|
|
# Check approximate normal distribution properties
|
|
assert abs(tensor.data.mean()) < 0.2 # Mean ~= 0
|
|
assert 0.8 < tensor.data.std() < 1.2 # Std ~= 1
|
|
|
|
|
|
class TestTensorOperations:
|
|
"""Tests for tensor operations."""
|
|
|
|
def test_reshape(self):
|
|
"""Should reshape tensor."""
|
|
tensor = Tensor.from_numpy(np.arange(6))
|
|
reshaped = tensor.reshape((2, 3))
|
|
|
|
assert reshaped.shape == (2, 3)
|
|
assert reshaped.size == 6
|
|
|
|
def test_reshape_preserves_data(self):
|
|
"""Should preserve data when reshaping."""
|
|
arr = np.array([1, 2, 3, 4, 5, 6])
|
|
tensor = Tensor.from_numpy(arr)
|
|
reshaped = tensor.reshape((2, 3))
|
|
|
|
assert reshaped.data[0, 0] == 1
|
|
assert reshaped.data[1, 2] == 6
|
|
|
|
def test_to_different_dtype(self):
|
|
"""Should convert to different dtype."""
|
|
tensor = Tensor.from_numpy(np.array([1.5, 2.5], dtype=np.float32))
|
|
converted = tensor.to(Precision.FP64)
|
|
|
|
assert converted.dtype == Precision.FP64
|
|
assert converted.data.dtype == np.float64
|
|
|
|
def test_to_same_dtype_returns_self(self):
|
|
"""Should return self when converting to same dtype."""
|
|
tensor = Tensor.from_numpy(np.array([1.0, 2.0], dtype=np.float32))
|
|
same = tensor.to(Precision.FP32)
|
|
|
|
assert same is tensor
|
|
|
|
def test_numpy_returns_underlying_array(self):
|
|
"""Should return underlying numpy array."""
|
|
arr = np.array([1, 2, 3], dtype=np.float32)
|
|
tensor = Tensor.from_numpy(arr)
|
|
|
|
assert np.array_equal(tensor.numpy(), arr)
|
|
|
|
|
|
class TestTensorProperties:
|
|
"""Tests for tensor properties."""
|
|
|
|
def test_shape(self):
|
|
"""Should return correct shape."""
|
|
tensor = Tensor.zeros((3, 4, 5))
|
|
assert tensor.shape == (3, 4, 5)
|
|
|
|
def test_size(self):
|
|
"""Should return correct size."""
|
|
tensor = Tensor.zeros((3, 4, 5))
|
|
assert tensor.size == 60
|
|
|
|
def test_ndim(self):
|
|
"""Should return correct number of dimensions."""
|
|
tensor = Tensor.zeros((3, 4, 5))
|
|
assert tensor.ndim == 3
|
|
|
|
def test_nbytes(self):
|
|
"""Should return correct byte size."""
|
|
tensor = Tensor.zeros((10,), dtype=Precision.FP32)
|
|
assert tensor.nbytes == 40 # 10 * 4 bytes
|
|
|
|
|
|
class TestTensorSerialization:
|
|
"""Tests for tensor serialization."""
|
|
|
|
def test_serialize(self):
|
|
"""Should serialize tensor to dict."""
|
|
tensor = Tensor.from_numpy(np.array([[1, 2], [3, 4]], dtype=np.float32))
|
|
serialized = tensor.serialize()
|
|
|
|
assert "data" in serialized
|
|
assert "shape" in serialized
|
|
assert "dtype" in serialized
|
|
assert serialized["shape"] == [2, 2]
|
|
assert serialized["dtype"] == "fp32"
|
|
|
|
def test_deserialize(self):
|
|
"""Should deserialize tensor from dict."""
|
|
original = Tensor.from_numpy(np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32))
|
|
serialized = original.serialize()
|
|
restored = Tensor.deserialize(serialized)
|
|
|
|
assert restored.shape == original.shape
|
|
assert restored.dtype == original.dtype
|
|
assert np.array_equal(restored.data, original.data)
|
|
|
|
def test_serialize_deserialize_fp64(self):
|
|
"""Should preserve FP64 precision."""
|
|
original = Tensor.from_numpy(np.array([1.234567890123456], dtype=np.float64))
|
|
serialized = original.serialize()
|
|
restored = Tensor.deserialize(serialized)
|
|
|
|
assert restored.dtype == Precision.FP64
|
|
assert np.allclose(restored.data, original.data)
|
|
|
|
def test_serialize_deserialize_large_tensor(self):
|
|
"""Should handle large tensors."""
|
|
original = Tensor.random((100, 100))
|
|
serialized = original.serialize()
|
|
restored = Tensor.deserialize(serialized)
|
|
|
|
assert restored.shape == original.shape
|
|
assert np.allclose(restored.data, original.data)
|
|
|
|
|
|
class TestTensorRepr:
|
|
"""Tests for tensor string representations."""
|
|
|
|
def test_repr(self):
|
|
"""Should return informative repr."""
|
|
tensor = Tensor.zeros((2, 3), dtype=Precision.FP32)
|
|
rep = repr(tensor)
|
|
|
|
assert "Tensor" in rep
|
|
assert "(2, 3)" in rep
|
|
assert "fp32" in rep
|
|
|
|
def test_str(self):
|
|
"""Should return string with data."""
|
|
tensor = Tensor.from_numpy(np.array([1, 2, 3], dtype=np.float32))
|
|
s = str(tensor)
|
|
|
|
assert "Tensor" in s
|
|
assert "fp32" in s
|
|
|
|
|
|
class TestTensorEdgeCases:
|
|
"""Tests for edge cases."""
|
|
|
|
def test_scalar_like_tensor(self):
|
|
"""Should handle 1-element tensor."""
|
|
tensor = Tensor.from_numpy(np.array([42], dtype=np.float32))
|
|
|
|
assert tensor.shape == (1,)
|
|
assert tensor.size == 1
|
|
assert tensor.ndim == 1
|
|
|
|
def test_empty_shape_dimension(self):
|
|
"""Should handle tensors with zero-size dimensions."""
|
|
tensor = Tensor.zeros((0, 3))
|
|
|
|
assert tensor.shape == (0, 3)
|
|
assert tensor.size == 0
|
|
|
|
def test_high_dimensional_tensor(self):
|
|
"""Should handle high-dimensional tensors."""
|
|
tensor = Tensor.zeros((2, 3, 4, 5, 6))
|
|
|
|
assert tensor.ndim == 5
|
|
assert tensor.size == 720
|