import numpy as np
import pytest
from astrora._core import numpy_ops
class TestBasicArrayOperations:
def test_sum_array(self):
arr = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
result = numpy_ops.sum_array(arr)
assert result == pytest.approx(15.0)
def test_sum_array_empty(self):
arr = np.array([])
result = numpy_ops.sum_array(arr)
assert result == 0.0
def test_sum_array_single_element(self):
arr = np.array([42.0])
result = numpy_ops.sum_array(arr)
assert result == 42.0
def test_multiply_scalar(self):
arr = np.array([1.0, 2.0, 3.0])
result = numpy_ops.multiply_scalar(arr, 2.5)
assert np.array_equal(arr, [1.0, 2.0, 3.0])
assert np.allclose(result, [2.5, 5.0, 7.5])
def test_multiply_scalar_inplace(self):
arr = np.array([1.0, 2.0, 3.0])
original_id = id(arr)
numpy_ops.multiply_scalar_inplace(arr, 2.0)
assert np.allclose(arr, [2.0, 4.0, 6.0])
assert id(arr) == original_id
def test_multiply_scalar_negative(self):
arr = np.array([1.0, -2.0, 3.0])
result = numpy_ops.multiply_scalar(arr, -1.5)
assert np.allclose(result, [-1.5, 3.0, -4.5])
class TestVectorOperations:
def test_dot_product_basic(self):
a = np.array([1.0, 2.0, 3.0])
b = np.array([4.0, 5.0, 6.0])
result = numpy_ops.dot_product(a, b)
assert result == pytest.approx(32.0)
def test_dot_product_orthogonal(self):
a = np.array([1.0, 0.0, 0.0])
b = np.array([0.0, 1.0, 0.0])
result = numpy_ops.dot_product(a, b)
assert result == pytest.approx(0.0)
def test_dot_product_length_mismatch(self):
a = np.array([1.0, 2.0])
b = np.array([1.0, 2.0, 3.0])
with pytest.raises(ValueError):
numpy_ops.dot_product(a, b)
def test_cross_product_basis_vectors(self):
x = np.array([1.0, 0.0, 0.0])
y = np.array([0.0, 1.0, 0.0])
z = numpy_ops.cross_product(x, y)
assert np.allclose(z, [0.0, 0.0, 1.0])
def test_cross_product_anticommutative(self):
a = np.array([1.0, 2.0, 3.0])
b = np.array([4.0, 5.0, 6.0])
ab = numpy_ops.cross_product(a, b)
ba = numpy_ops.cross_product(b, a)
assert np.allclose(ab, -ba)
def test_cross_product_invalid_length(self):
a = np.array([1.0, 2.0])
b = np.array([3.0, 4.0, 5.0])
with pytest.raises(ValueError):
numpy_ops.cross_product(a, b)
def test_add_arrays(self):
a = np.array([1.0, 2.0, 3.0])
b = np.array([4.0, 5.0, 6.0])
result = numpy_ops.add_arrays(a, b)
assert np.allclose(result, [5.0, 7.0, 9.0])
def test_add_arrays_length_mismatch(self):
a = np.array([1.0, 2.0])
b = np.array([1.0, 2.0, 3.0])
with pytest.raises(ValueError):
numpy_ops.add_arrays(a, b)
def test_normalize_vector(self):
vec = np.array([3.0, 4.0, 0.0])
result = numpy_ops.normalize_vector(vec)
magnitude = np.linalg.norm(result)
assert magnitude == pytest.approx(1.0)
assert np.allclose(result, [0.6, 0.8, 0.0])
def test_normalize_already_unit_vector(self):
vec = np.array([1.0, 0.0, 0.0])
result = numpy_ops.normalize_vector(vec)
assert np.allclose(result, vec)
def test_normalize_zero_vector(self):
vec = np.array([0.0, 0.0, 0.0])
with pytest.raises(ArithmeticError): numpy_ops.normalize_vector(vec)
def test_vector_magnitude(self):
vec = np.array([3.0, 4.0, 0.0])
result = numpy_ops.vector_magnitude(vec)
assert result == pytest.approx(5.0)
def test_vector_magnitude_unit_vector(self):
vec = np.array([1.0, 0.0, 0.0])
result = numpy_ops.vector_magnitude(vec)
assert result == pytest.approx(1.0)
class TestMatrixOperations:
def test_matrix_vector_multiply(self):
matrix = np.array([[1.0, 2.0], [3.0, 4.0]])
vector = np.array([5.0, 6.0])
result = numpy_ops.matrix_vector_multiply(matrix, vector)
assert np.allclose(result, [17.0, 39.0])
def test_matrix_vector_multiply_identity(self):
identity = np.eye(3)
vector = np.array([1.0, 2.0, 3.0])
result = numpy_ops.matrix_vector_multiply(identity, vector)
assert np.allclose(result, vector)
def test_matrix_vector_multiply_dimension_mismatch(self):
matrix = np.array([[1.0, 2.0], [3.0, 4.0]])
vector = np.array([5.0, 6.0, 7.0])
with pytest.raises(ValueError):
numpy_ops.matrix_vector_multiply(matrix, vector)
def test_matrix_multiply(self):
a = np.array([[1.0, 2.0], [3.0, 4.0]])
b = np.array([[5.0, 6.0], [7.0, 8.0]])
result = numpy_ops.matrix_multiply(a, b)
expected = np.array([[19.0, 22.0], [43.0, 50.0]])
assert np.allclose(result, expected)
def test_matrix_multiply_identity(self):
a = np.array([[1.0, 2.0], [3.0, 4.0]])
identity = np.eye(2)
result = numpy_ops.matrix_multiply(a, identity)
assert np.allclose(result, a)
def test_matrix_multiply_dimension_mismatch(self):
a = np.array([[1.0, 2.0]]) b = np.array([[3.0], [4.0], [5.0]]) with pytest.raises(ValueError):
numpy_ops.matrix_multiply(a, b)
def test_transpose_matrix(self):
matrix = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
result = numpy_ops.transpose_matrix(matrix)
expected = np.array([[1.0, 4.0], [2.0, 5.0], [3.0, 6.0]])
assert np.allclose(result, expected)
def test_transpose_square_matrix(self):
matrix = np.array([[1.0, 2.0], [3.0, 4.0]])
result = numpy_ops.transpose_matrix(matrix)
expected = np.array([[1.0, 3.0], [2.0, 4.0]])
assert np.allclose(result, expected)
def test_identity_matrix(self):
result = numpy_ops.identity_matrix(3)
expected = np.eye(3)
assert np.allclose(result, expected)
def test_identity_matrix_size_1(self):
result = numpy_ops.identity_matrix(1)
assert np.allclose(result, [[1.0]])
def test_identity_matrix_zero_size(self):
with pytest.raises(ValueError):
numpy_ops.identity_matrix(0)
class TestPolynomialOperations:
def test_apply_polynomial(self):
arr = np.array([0.0, 1.0, 2.0])
result = numpy_ops.apply_polynomial(arr)
assert np.allclose(result, [1.0, 4.0, 9.0])
def test_apply_polynomial_negative(self):
arr = np.array([-1.0])
result = numpy_ops.apply_polynomial(arr)
assert np.allclose(result, [0.0])
class TestBatchOperations:
def test_batch_normalize_vectors(self):
vectors = np.array([[3.0, 4.0, 0.0], [0.0, 0.0, 5.0], [1.0, 1.0, 1.0]])
result = numpy_ops.batch_normalize_vectors(vectors)
for i in range(3):
magnitude = np.linalg.norm(result[i, :])
assert magnitude == pytest.approx(1.0)
assert np.allclose(result[0, :], [0.6, 0.8, 0.0])
assert np.allclose(result[1, :], [0.0, 0.0, 1.0])
sqrt3_inv = 1.0 / np.sqrt(3.0)
assert np.allclose(result[2, :], [sqrt3_inv, sqrt3_inv, sqrt3_inv])
def test_batch_normalize_single_vector(self):
vectors = np.array([[3.0, 4.0, 0.0]])
result = numpy_ops.batch_normalize_vectors(vectors)
assert np.allclose(result[0, :], [0.6, 0.8, 0.0])
def test_batch_normalize_wrong_dimensions(self):
vectors = np.array([[1.0, 2.0], [3.0, 4.0]]) with pytest.raises(ValueError):
numpy_ops.batch_normalize_vectors(vectors)
def test_batch_normalize_with_zero_vector(self):
vectors = np.array([[3.0, 4.0, 0.0], [0.0, 0.0, 0.0], [1.0, 1.0, 1.0]]) with pytest.raises(ArithmeticError):
numpy_ops.batch_normalize_vectors(vectors)
class TestDataTypes:
def test_float32_input(self):
arr = np.array([1.0, 2.0, 3.0], dtype=np.float32)
result = numpy_ops.sum_array(arr.astype(np.float64))
assert result == pytest.approx(6.0)
def test_integer_input_conversion(self):
arr = np.array([1, 2, 3], dtype=np.int32)
result = numpy_ops.sum_array(arr.astype(np.float64))
assert result == pytest.approx(6.0)
class TestEdgeCases:
def test_very_small_values(self):
arr = np.array([1e-10, 2e-10, 3e-10])
result = numpy_ops.sum_array(arr)
assert result == pytest.approx(6e-10)
def test_very_large_values(self):
arr = np.array([1e10, 2e10, 3e10])
result = numpy_ops.sum_array(arr)
assert result == pytest.approx(6e10)
def test_mixed_signs(self):
arr = np.array([-5.0, 10.0, -3.0, 8.0])
result = numpy_ops.sum_array(arr)
assert result == pytest.approx(10.0)
def test_large_array_performance(self):
arr = np.random.randn(10000)
result = numpy_ops.sum_array(arr)
expected = np.sum(arr)
assert result == pytest.approx(expected)
class TestZeroCopyBehavior:
def test_readonly_preserves_original(self):
arr = np.array([1.0, 2.0, 3.0])
original_copy = arr.copy()
_ = numpy_ops.sum_array(arr)
_ = numpy_ops.multiply_scalar(arr, 2.0)
_ = numpy_ops.vector_magnitude(arr)
assert np.array_equal(arr, original_copy)
def test_inplace_modifies_original(self):
arr = np.array([1.0, 2.0, 3.0])
numpy_ops.multiply_scalar_inplace(arr, 2.0)
assert np.allclose(arr, [2.0, 4.0, 6.0])
def test_contiguous_vs_non_contiguous(self):
arr_c = np.array([1.0, 2.0, 3.0])
result_c = numpy_ops.sum_array(arr_c)
arr_full = np.array([1.0, 999.0, 2.0, 999.0, 3.0, 999.0])
arr_nc = arr_full[::2] result_nc = numpy_ops.sum_array(arr_nc)
assert result_c == result_nc
if __name__ == "__main__":
pytest.main([__file__, "-v"])