NumPy & SciPy compatible GPU-accelerated array library for CUDA computing
—
Comprehensive testing framework with NumPy comparison utilities, array assertions, and performance benchmarking tools for development and validation. CuPy provides extensive testing infrastructure for ensuring correctness and performance of GPU-accelerated code.
Assertion functions for validating array properties and correctness in tests.
def assert_allclose(actual, desired, rtol=1e-7, atol=0, err_msg='', verbose=True):
"""Assert arrays are element-wise equal within tolerance.
Args:
actual: Actual array
desired: Desired array
rtol: Relative tolerance
atol: Absolute tolerance
err_msg: Error message for assertion failure
verbose: Print conflicting values on failure
Raises:
AssertionError: If arrays are not close within tolerance
"""
def assert_array_equal(x, y, err_msg='', verbose=True, strides_check=False):
"""Assert arrays are exactly equal.
Args:
x: First input array
y: Second input array
err_msg: Error message for assertion failure
verbose: Print conflicting values on failure
strides_check: Check array strides are equal
Raises:
AssertionError: If arrays are not equal
"""
def assert_array_almost_equal(x, y, decimal=6, err_msg='', verbose=True):
"""Assert arrays are equal up to specified precision.
Args:
x: First input array
y: Second input array
decimal: Number of decimal places for comparison
err_msg: Error message for assertion failure
verbose: Print conflicting values on failure
Raises:
AssertionError: If arrays are not almost equal
"""
def assert_array_less(x, y, err_msg='', verbose=True):
"""Assert array elements are less than corresponding elements.
Args:
x: First input array
y: Second input array
err_msg: Error message for assertion failure
verbose: Print conflicting values on failure
Raises:
AssertionError: If condition is not satisfied
"""
def assert_array_list_equal(xlist, ylist, err_msg='', verbose=True):
"""Assert lists of arrays are equal.
Args:
xlist: First list of arrays
ylist: Second list of arrays
err_msg: Error message for assertion failure
verbose: Print conflicting values on failure
Raises:
AssertionError: If array lists are not equal
"""Decorators for testing CuPy functions against NumPy equivalents with automatic array module switching.
def numpy_cupy_allclose(rtol=1e-7, atol=0, err_msg='', verbose=True,
name='xp', type_check=True, accept_error=False,
sp_name=None, scipy_name=None, contiguous_check=True,
strides_check=False):
"""Decorator for testing CuPy vs NumPy with allclose comparison.
Args:
rtol: Relative tolerance
atol: Absolute tolerance
err_msg: Error message
verbose: Print details on failure
name: Parameter name for array module (xp)
type_check: Check result types match
accept_error: Accept errors as long as both raise same error
sp_name: Parameter name for sparse module
scipy_name: Parameter name for scipy module
contiguous_check: Check array contiguity
strides_check: Check array strides
Example:
@numpy_cupy_allclose()
def test_function(xp):
a = xp.array([1, 2, 3])
return xp.sum(a)
"""
def numpy_cupy_array_equal(err_msg='', verbose=True, name='xp',
type_check=True, accept_error=False,
sp_name=None, scipy_name=None, strides_check=False):
"""Decorator for testing CuPy vs NumPy with exact equality.
Args:
err_msg: Error message
verbose: Print details on failure
name: Parameter name for array module
type_check: Check result types match
accept_error: Accept matching errors
sp_name: Parameter name for sparse module
scipy_name: Parameter name for scipy module
strides_check: Check array strides
Example:
@numpy_cupy_array_equal()
def test_integer_function(xp):
a = xp.array([1, 2, 3])
return xp.argmax(a)
"""
def numpy_cupy_raises(accept_error=Exception, name='xp', sp_name=None, scipy_name=None):
"""Decorator for testing that both NumPy and CuPy raise exceptions.
Args:
accept_error: Expected exception type(s)
name: Parameter name for array module
sp_name: Parameter name for sparse module
scipy_name: Parameter name for scipy module
Example:
@numpy_cupy_raises()
def test_invalid_operation(xp):
a = xp.array([1, 2, 3])
return a / xp.array([0, 1, 2]) # Division by zero
"""Functions for generating test arrays with specific properties and patterns.
def shaped_arange(shape, xp=None, dtype=float, order='C'):
"""Generate array with arange values in specified shape.
Args:
shape: Output array shape
xp: Array module (numpy or cupy)
dtype: Data type
order: Memory layout
Returns:
Array: Shaped arange array
Example:
arr = shaped_arange((2, 3), xp=cp, dtype=int)
# Returns: [[0, 1, 2], [3, 4, 5]]
"""
def shaped_random(shape, xp=None, dtype=float, seed=0, scale=10):
"""Generate random array with specified properties.
Args:
shape: Output array shape
xp: Array module (numpy or cupy)
dtype: Data type
seed: Random seed for reproducibility
scale: Scale factor for random values
Returns:
Array: Random array with specified shape and properties
"""
def shaped_reverse_arange(shape, xp=None, dtype=float):
"""Generate array with reverse arange values.
Args:
shape: Output array shape
xp: Array module
dtype: Data type
Returns:
Array: Reverse arange array
"""
def for_dtypes(dtypes, name='dtype'):
"""Parameterize test for multiple data types.
Args:
dtypes: List of data types to test
name: Parameter name for dtype
Returns:
Decorator: Parameterized test decorator
"""
def for_all_dtypes(name='dtype', no_float16=False, no_bool=False,
no_complex=False, additional_args=()):
"""Parameterize test for all standard data types.
Args:
name: Parameter name for dtype
no_float16: Exclude float16 type
no_bool: Exclude boolean type
no_complex: Exclude complex types
additional_args: Additional test parameters
Returns:
Decorator: Parameterized test decorator
"""
def for_float_dtypes(name='dtype', no_float16=False, additional_args=()):
"""Parameterize test for floating-point data types.
Args:
name: Parameter name for dtype
no_float16: Exclude float16 type
additional_args: Additional test parameters
Returns:
Decorator: Parameterized test decorator
"""
def for_int_dtypes(name='dtype', no_bool=False, additional_args=()):
"""Parameterize test for integer data types.
Args:
name: Parameter name for dtype
no_bool: Exclude boolean type
additional_args: Additional test parameters
Returns:
Decorator: Parameterized test decorator
"""
def for_signed_dtypes(name='dtype', additional_args=()):
"""Parameterize test for signed data types."""
def for_unsigned_dtypes(name='dtype', additional_args=()):
"""Parameterize test for unsigned data types."""
def for_complex_dtypes(name='dtype', additional_args=()):
"""Parameterize test for complex data types."""Advanced parameterization utilities for comprehensive test coverage.
def parameterize(*params, **kwargs):
"""Parameterize test with multiple parameter combinations.
Args:
*params: Parameter specifications
**kwargs: Named parameter specifications
Returns:
Decorator: Parameterized test decorator
Example:
@parameterize([
{'shape': (10,), 'axis': None},
{'shape': (3, 4), 'axis': 0},
{'shape': (2, 3, 4), 'axis': (1, 2)},
])
def test_reduction(self, shape, axis):
...
"""
def product(parameter_dict):
"""Generate Cartesian product of parameters.
Args:
parameter_dict: Dictionary of parameter lists
Returns:
Decorator: Cartesian product test decorator
Example:
@product({
'shape': [(10,), (3, 4), (2, 3, 4)],
'dtype': [float32, float64],
'axis': [None, 0, -1]
})
def test_function(self, shape, dtype, axis):
...
"""
def product_dict(*dicts):
"""Generate Cartesian product from multiple dictionaries.
Args:
*dicts: Parameter dictionaries to combine
Returns:
Decorator: Combined parameter test decorator
"""Utilities for benchmarking and performance validation.
def time_cupy_vs_numpy(func, args=(), number=1, repeat=3, setup='pass'):
"""Compare execution time between CuPy and NumPy.
Args:
func: Function to benchmark
args: Function arguments
number: Number of executions per timing
repeat: Number of timing repetitions
setup: Setup code
Returns:
dict: Timing results for both libraries
"""
class Timer:
"""High-resolution timer for performance measurements.
Example:
with Timer() as timer:
result = expensive_operation()
print(f"Operation took {timer.elapsed:.3f} seconds")
"""
def __enter__(self):
self.start_time = time.perf_counter()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.end_time = time.perf_counter()
self.elapsed = self.end_time - self.start_time
def benchmark_function(func, args=(), warmup=3, number=10):
"""Benchmark function execution time.
Args:
func: Function to benchmark
args: Function arguments
warmup: Number of warmup runs
number: Number of timing runs
Returns:
dict: Benchmark statistics (mean, std, min, max)
"""Test execution control and resource management utilities.
def gpu(*args, **kwargs):
"""Mark test as requiring GPU.
Args:
*args: GPU requirements
**kwargs: Additional GPU parameters
Returns:
Decorator: GPU test marker
"""
def multi_gpu(gpu_num):
"""Mark test as requiring multiple GPUs.
Args:
gpu_num: Number of GPUs required
Returns:
Decorator: Multi-GPU test marker
"""
def slow():
"""Mark test as slow-running.
Returns:
Decorator: Slow test marker
"""
def condition(cond):
"""Conditional test execution.
Args:
cond: Boolean condition for test execution
Returns:
Decorator: Conditional test decorator
"""
def repeat(n):
"""Repeat test execution n times.
Args:
n: Number of repetitions
Returns:
Decorator: Test repetition decorator
"""Utilities for testing error conditions and input validation.
def raises(exception_type, match=None):
"""Context manager for testing exceptions.
Args:
exception_type: Expected exception type
match: Regex pattern for exception message
Returns:
Context manager for exception testing
Example:
with raises(ValueError, match="invalid shape"):
cp.array([1, 2, 3]).reshape((2, 2))
"""
def warns(warning_type, match=None):
"""Context manager for testing warnings.
Args:
warning_type: Expected warning type
match: Regex pattern for warning message
Returns:
Context manager for warning testing
"""
def assert_warns(warning_class, func, *args, **kwargs):
"""Assert function raises specific warning.
Args:
warning_class: Expected warning type
func: Function to test
*args: Function arguments
**kwargs: Function keyword arguments
Returns:
Function result if warning is raised
"""
def suppress_warnings():
"""Context manager for suppressing warnings during tests.
Returns:
Context manager for warning suppression
"""Specialized utilities for CuPy-specific testing scenarios.
def assert_array_equal_ex(x, y, strides_check=False):
"""Extended array equality with stride checking.
Args:
x: First array
y: Second array
strides_check: Check stride equality
Raises:
AssertionError: If arrays differ
"""
def generate_matrix(shape, dtype, matrix_type='random', seed=None):
"""Generate test matrix with specific properties.
Args:
shape: Matrix shape
dtype: Data type
matrix_type: Matrix type ('random', 'identity', 'zeros', 'ones', 'diagonal')
seed: Random seed
Returns:
cupy.ndarray: Generated test matrix
"""
def check_elementwise_kernel(kernel_func, numpy_func, test_cases, rtol=1e-7, atol=0):
"""Validate custom elementwise kernel against NumPy function.
Args:
kernel_func: CuPy kernel function
numpy_func: Reference NumPy function
test_cases: List of test input arrays
rtol: Relative tolerance
atol: Absolute tolerance
Raises:
AssertionError: If kernel results differ from NumPy
"""
def check_reduction_kernel(kernel_func, numpy_func, test_cases, axes_list=None):
"""Validate custom reduction kernel against NumPy function.
Args:
kernel_func: CuPy reduction kernel
numpy_func: Reference NumPy function
test_cases: List of test input arrays
axes_list: List of axes to test
Raises:
AssertionError: If kernel results differ from NumPy
"""import cupy as cp
import numpy as np
from cupy.testing import assert_allclose, assert_array_equal
# Test basic array operations
def test_array_creation():
"""Test array creation functions."""
# Test zeros
cupy_zeros = cp.zeros((3, 4), dtype=cp.float32)
numpy_zeros = np.zeros((3, 4), dtype=np.float32)
assert_array_equal(cupy_zeros, numpy_zeros)
print("✓ Array creation test passed")
def test_mathematical_operations():
"""Test mathematical operations."""
# Create test data
np.random.seed(42)
data_np = np.random.randn(100, 50).astype(np.float32)
data_cp = cp.asarray(data_np)
# Test sum operation
sum_np = np.sum(data_np, axis=1)
sum_cp = cp.sum(data_cp, axis=1)
assert_allclose(sum_cp, sum_np, rtol=1e-6)
print("✓ Sum operation test passed")
# Test complex operation
result_np = np.sqrt(np.sum(data_np**2, axis=1))
result_cp = cp.sqrt(cp.sum(data_cp**2, axis=1))
assert_allclose(result_cp, result_np, rtol=1e-6)
print("✓ Complex operation test passed")
# Run basic tests
test_array_creation()
test_mathematical_operations()import cupy as cp
from cupy.testing import numpy_cupy_allclose, numpy_cupy_array_equal, shaped_arange
class TestMathFunctions:
"""Test mathematical functions using comparison decorators."""
@numpy_cupy_allclose(rtol=1e-6)
def test_trigonometric_functions(self, xp):
"""Test trigonometric functions."""
x = shaped_arange((100,), xp, dtype=xp.float32)
x = x / 10 # Scale to reasonable range
# Test multiple trig functions
sin_result = xp.sin(x)
cos_result = xp.cos(x)
combined = sin_result**2 + cos_result**2 # Should be close to 1
return combined
@numpy_cupy_allclose(rtol=1e-5)
def test_reduction_operations(self, xp):
"""Test reduction operations."""
data = shaped_arange((50, 40), xp, dtype=xp.float64)
# Test various reductions
results = {
'sum_all': xp.sum(data),
'sum_axis0': xp.sum(data, axis=0),
'sum_axis1': xp.sum(data, axis=1),
'mean_all': xp.mean(data),
'std_all': xp.std(data),
}
return results
@numpy_cupy_array_equal()
def test_indexing_operations(self, xp):
"""Test indexing operations (exact equality expected)."""
data = shaped_arange((20, 30), xp, dtype=xp.int32)
# Test various indexing patterns
results = {
'slice1': data[5:15, 10:25],
'stride': data[::2, ::3],
'boolean': data[data > 200],
'fancy': data[[1, 3, 5], [2, 4, 6]],
}
return results
# Run comparison tests
test_class = TestMathFunctions()
print("Running NumPy comparison tests...")
try:
result = test_class.test_trigonometric_functions()
print("✓ Trigonometric functions test passed")
except Exception as e:
print(f"✗ Trigonometric functions test failed: {e}")
try:
result = test_class.test_reduction_operations()
print("✓ Reduction operations test passed")
except Exception as e:
print(f"✗ Reduction operations test failed: {e}")
try:
result = test_class.test_indexing_operations()
print("✓ Indexing operations test passed")
except Exception as e:
print(f"✗ Indexing operations test failed: {e}")import cupy as cp
from cupy.testing import for_all_dtypes, for_float_dtypes, parameterize, shaped_arange
class TestParameterizedFunctions:
"""Test functions with multiple parameter combinations."""
@for_all_dtypes(no_complex=True)
def test_array_creation_dtypes(self, dtype):
"""Test array creation for all data types."""
# Create array with specific dtype
arr = cp.zeros(100, dtype=dtype)
# Verify dtype
assert arr.dtype == dtype
# Verify values
expected = cp.zeros(100, dtype=dtype)
cp.testing.assert_array_equal(arr, expected)
print(f"✓ Array creation test passed for dtype: {dtype}")
@for_float_dtypes()
def test_mathematical_precision(self, dtype):
"""Test mathematical operations for floating-point types."""
# Generate test data
data = shaped_arange((50,), dtype=dtype) / 10
# Test operation that requires precision
result = cp.exp(cp.log(data + 1)) # Should equal data + 1
expected = data + 1
# Adjust tolerance based on dtype precision
if dtype == cp.float16:
rtol = 1e-3
elif dtype == cp.float32:
rtol = 1e-6
else: # float64
rtol = 1e-12
cp.testing.assert_allclose(result, expected, rtol=rtol)
print(f"✓ Mathematical precision test passed for dtype: {dtype}")
@parameterize([
{'shape': (100,), 'axis': None},
{'shape': (10, 10), 'axis': 0},
{'shape': (10, 10), 'axis': 1},
{'shape': (5, 4, 3), 'axis': (0, 2)},
{'shape': (2, 3, 4, 5), 'axis': None},
])
def test_sum_various_shapes(self, shape, axis):
"""Test sum operation for various shapes and axes."""
# Create test array
arr = shaped_arange(shape, cp, dtype=cp.float32)
# Compute sum
result = cp.sum(arr, axis=axis)
# Verify result properties
if axis is None:
assert result.ndim == 0 # Scalar result
else:
# Check output shape
expected_shape = list(shape)
if isinstance(axis, int):
expected_shape.pop(axis)
else:
for ax in sorted(axis, reverse=True):
expected_shape.pop(ax)
expected_shape = tuple(expected_shape) if expected_shape else ()
assert result.shape == expected_shape
print(f"✓ Sum test passed for shape: {shape}, axis: {axis}")
# Run parameterized tests
test_class = TestParameterizedFunctions()
# Test all data types
print("Testing array creation for all data types:")
for dtype in [cp.int8, cp.int16, cp.int32, cp.int64,
cp.float16, cp.float32, cp.float64, cp.bool_]:
try:
test_class.test_array_creation_dtypes(dtype)
except Exception as e:
print(f"✗ Failed for dtype {dtype}: {e}")
# Test floating-point precision
print("\nTesting mathematical precision for float types:")
for dtype in [cp.float16, cp.float32, cp.float64]:
try:
test_class.test_mathematical_precision(dtype)
except Exception as e:
print(f"✗ Failed for dtype {dtype}: {e}")
# Test various shapes manually (simulating parameterized test)
print("\nTesting sum operation for various shapes:")
test_params = [
{'shape': (100,), 'axis': None},
{'shape': (10, 10), 'axis': 0},
{'shape': (10, 10), 'axis': 1},
{'shape': (5, 4, 3), 'axis': (0, 2)},
{'shape': (2, 3, 4, 5), 'axis': None},
]
for params in test_params:
try:
test_class.test_sum_various_shapes(**params)
except Exception as e:
print(f"✗ Failed for params {params}: {e}")import cupy as cp
import numpy as np
import time
class PerformanceTester:
"""Performance testing utilities."""
def __init__(self):
self.warmup_runs = 3
self.timing_runs = 10
def benchmark_function(self, func, args=(), warmup=None, runs=None):
"""Benchmark function execution time."""
warmup = warmup or self.warmup_runs
runs = runs or self.timing_runs
# Warmup runs
for _ in range(warmup):
result = func(*args)
if hasattr(cp, 'cuda'):
cp.cuda.Stream.null.synchronize()
# Timing runs
times = []
for _ in range(runs):
start_time = time.perf_counter()
result = func(*args)
if hasattr(cp, 'cuda'):
cp.cuda.Stream.null.synchronize()
end_time = time.perf_counter()
times.append(end_time - start_time)
return {
'mean': np.mean(times),
'std': np.std(times),
'min': np.min(times),
'max': np.max(times),
'times': times,
'result': result
}
def compare_cupy_numpy(self, operation_name, cupy_func, numpy_func,
cupy_args=(), numpy_args=None):
"""Compare CuPy and NumPy performance."""
numpy_args = numpy_args or cupy_args
print(f"\nBenchmarking {operation_name}:")
# Benchmark NumPy
numpy_stats = self.benchmark_function(numpy_func, numpy_args)
# Benchmark CuPy
cupy_stats = self.benchmark_function(cupy_func, cupy_args)
# Calculate speedup
speedup = numpy_stats['mean'] / cupy_stats['mean']
print(f" NumPy: {numpy_stats['mean']*1000:6.2f} ± {numpy_stats['std']*1000:5.2f} ms")
print(f" CuPy: {cupy_stats['mean']*1000:6.2f} ± {cupy_stats['std']*1000:5.2f} ms")
print(f" Speedup: {speedup:.2f}x")
return {
'numpy': numpy_stats,
'cupy': cupy_stats,
'speedup': speedup
}
# Performance testing examples
tester = PerformanceTester()
# Large matrix operations
n = 5000
print(f"Performance testing with matrices of size {n}x{n}")
# Matrix multiplication
A_np = np.random.randn(n, n).astype(np.float32)
B_np = np.random.randn(n, n).astype(np.float32)
A_cp = cp.asarray(A_np)
B_cp = cp.asarray(B_np)
results = tester.compare_cupy_numpy(
"Matrix Multiplication",
lambda: cp.dot(A_cp, B_cp),
lambda: np.dot(A_np, B_np)
)
# Element-wise operations
large_array_np = np.random.randn(10_000_000).astype(np.float32)
large_array_cp = cp.asarray(large_array_np)
results = tester.compare_cupy_numpy(
"Element-wise Sin",
lambda: cp.sin(large_array_cp),
lambda: np.sin(large_array_np)
)
results = tester.compare_cupy_numpy(
"Array Sum",
lambda: cp.sum(large_array_cp),
lambda: np.sum(large_array_np)
)
# FFT operations
fft_data_np = np.random.randn(2**20).astype(np.complex64)
fft_data_cp = cp.asarray(fft_data_np)
results = tester.compare_cupy_numpy(
"FFT",
lambda: cp.fft.fft(fft_data_cp),
lambda: np.fft.fft(fft_data_np)
)
# Memory bandwidth test
def memory_bandwidth_test():
"""Test memory bandwidth with various operations."""
sizes = [2**i for i in range(20, 28)] # From 1MB to 1GB
print("\nMemory Bandwidth Test:")
print("Size (MB) Copy (GB/s) Add (GB/s)")
for size in sizes:
n_elements = size
n_bytes = n_elements * 4 # float32
size_mb = n_bytes / (1024**2)
if size_mb > 500: # Skip very large sizes
break
# Generate data
a = cp.random.randn(n_elements).astype(cp.float32)
b = cp.random.randn(n_elements).astype(cp.float32)
# Test copy bandwidth
copy_stats = tester.benchmark_function(lambda: cp.copy(a), runs=5)
copy_bandwidth = (n_bytes / copy_stats['mean']) / (1024**3)
# Test add bandwidth (read a, read b, write result)
add_stats = tester.benchmark_function(lambda: a + b, runs=5)
add_bandwidth = (3 * n_bytes / add_stats['mean']) / (1024**3)
print(f"{size_mb:8.1f} {copy_bandwidth:8.1f} {add_bandwidth:8.1f}")
memory_bandwidth_test()import cupy as cp
from cupy import ElementwiseKernel, ReductionKernel
class CustomKernelTester:
"""Test custom CuPy kernels against reference implementations."""
def test_elementwise_kernel(self):
"""Test custom elementwise kernel."""
# Define custom kernel
square_diff_kernel = ElementwiseKernel(
'float32 x, float32 y',
'float32 z',
'z = (x - y) * (x - y)',
'square_diff'
)
# Generate test data
n = 10000
x = cp.random.randn(n).astype(cp.float32)
y = cp.random.randn(n).astype(cp.float32)
# Test kernel
kernel_result = square_diff_kernel(x, y)
# Reference implementation
reference_result = (x - y) ** 2
# Validate
cp.testing.assert_allclose(kernel_result, reference_result, rtol=1e-6)
print("✓ Elementwise kernel test passed")
return kernel_result
def test_reduction_kernel(self):
"""Test custom reduction kernel."""
# Define sum of squares kernel
sum_of_squares_kernel = ReductionKernel(
'float32 x',
'float32 y',
'x * x', # map expression
'a + b', # reduce expression
'y = a', # post expression
'0', # identity
'sum_of_squares'
)
# Generate test data
n = 10000
x = cp.random.randn(n).astype(cp.float32)
# Test kernel
kernel_result = sum_of_squares_kernel(x)
# Reference implementation
reference_result = cp.sum(x * x)
# Validate
cp.testing.assert_allclose(kernel_result, reference_result, rtol=1e-6)
print("✓ Reduction kernel test passed")
return kernel_result
def test_complex_kernel_workflow(self):
"""Test complex workflow combining multiple kernels."""
# Step 1: Normalize data
normalize_kernel = ElementwiseKernel(
'float32 x, float32 mean, float32 std',
'float32 z',
'z = (x - mean) / std',
'normalize'
)
# Step 2: Compute variance after normalization
variance_kernel = ReductionKernel(
'float32 x',
'float32 var',
'x * x',
'a + b',
'var = a / _in_ind.size()',
'0',
'variance'
)
# Generate test data
n = 50000
data = cp.random.normal(10, 3, n).astype(cp.float32)
# Compute statistics
mean_val = cp.mean(data)
std_val = cp.std(data)
# Apply normalization kernel
normalized = normalize_kernel(data, mean_val, std_val)
# Compute variance of normalized data (should be ~1)
variance = variance_kernel(normalized)
print(f"Original data: mean={float(mean_val):.3f}, std={float(std_val):.3f}")
print(f"Normalized data: mean={float(cp.mean(normalized)):.6f}, variance={float(variance):.6f}")
# Validate normalization
assert abs(float(cp.mean(normalized))) < 1e-6, "Mean should be ~0"
assert abs(float(variance) - 1.0) < 1e-3, "Variance should be ~1"
print("✓ Complex kernel workflow test passed")
return normalized, variance
# Run custom kernel tests
tester = CustomKernelTester()
try:
tester.test_elementwise_kernel()
tester.test_reduction_kernel()
tester.test_complex_kernel_workflow()
print("\n✓ All custom kernel tests passed!")
except Exception as e:
print(f"\n✗ Custom kernel test failed: {e}")import cupy as cp
from cupy.testing import assert_array_equal
import pytest
class ErrorTester:
"""Test error conditions and edge cases."""
def test_division_by_zero(self):
"""Test division by zero handling."""
# Test with different settings
a = cp.array([1.0, 2.0, 3.0])
b = cp.array([1.0, 0.0, 2.0])
# Should produce inf without error
result = a / b
expected = cp.array([1.0, cp.inf, 1.5])
cp.testing.assert_array_equal(result, expected)
print("✓ Division by zero test passed")
def test_invalid_shapes(self):
"""Test operations with invalid shapes."""
a = cp.array([[1, 2], [3, 4]])
b = cp.array([1, 2, 3])
# This should raise an error
try:
result = a @ b # Invalid matrix multiplication
assert False, "Expected ValueError for invalid shapes"
except ValueError:
print("✓ Invalid shape error test passed")
def test_out_of_bounds_indexing(self):
"""Test out-of-bounds indexing."""
arr = cp.arange(10)
# This should raise IndexError
try:
value = arr[15]
assert False, "Expected IndexError"
except IndexError:
print("✓ Out-of-bounds indexing test passed")
def test_dtype_overflow(self):
"""Test data type overflow conditions."""
# Test integer overflow
a = cp.array([127], dtype=cp.int8)
b = cp.array([1], dtype=cp.int8)
result = a + b # Should overflow
# Note: behavior may be platform dependent
print(f"Int8 overflow result: {result} (expected: -128 or wrapped value)")
# Test with larger types
large_val = cp.array([2**31 - 1], dtype=cp.int32)
overflow_result = large_val + cp.array([1], dtype=cp.int32)
print(f"Int32 overflow: {large_val} + 1 = {overflow_result}")
print("✓ Dtype overflow test completed")
def test_nan_propagation(self):
"""Test NaN propagation in calculations."""
data = cp.array([1.0, cp.nan, 3.0, 4.0])
# Test various operations
sum_result = cp.sum(data) # Should be NaN
nansum_result = cp.nansum(data) # Should ignore NaN
assert cp.isnan(sum_result), "Sum should propagate NaN"
assert not cp.isnan(nansum_result), "nansum should ignore NaN"
assert cp.isclose(nansum_result, 8.0), f"nansum should equal 8, got {nansum_result}"
print("✓ NaN propagation test passed")
def test_memory_limits(self):
"""Test behavior near memory limits."""
# Try to allocate very large array
try:
# This will likely fail on most systems
huge_array = cp.zeros((100000, 100000), dtype=cp.float64)
print(f"Successfully allocated huge array: {huge_array.shape}")
except cp.cuda.memory.OutOfMemoryError:
print("✓ Out of memory error handled correctly")
except MemoryError:
print("✓ Memory error handled correctly")
# Test gradual memory allocation
arrays = []
try:
for i in range(100):
arr = cp.random.randn(1000, 1000)
arrays.append(arr)
if i % 20 == 0:
print(f"Allocated {i+1} arrays, total memory: ~{(i+1)*8:.1f} MB")
except (cp.cuda.memory.OutOfMemoryError, MemoryError):
print(f"Memory limit reached after {len(arrays)} arrays")
# Clean up
del arrays
cp.get_default_memory_pool().free_all_blocks()
print("✓ Memory cleanup completed")
# Run error tests
error_tester = ErrorTester()
print("Running error and edge case tests:")
try:
error_tester.test_division_by_zero()
error_tester.test_invalid_shapes()
error_tester.test_out_of_bounds_indexing()
error_tester.test_dtype_overflow()
error_tester.test_nan_propagation()
error_tester.test_memory_limits()
print("\n✓ All error handling tests completed successfully!")
except Exception as e:
print(f"\n✗ Error in error testing: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-cupy-cuda114