CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cupy-cuda114

NumPy & SciPy compatible GPU-accelerated array library for CUDA computing

Pending
Overview
Eval results
Files

testing.mddocs/

Testing and Debugging Framework

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.

Capabilities

Array Assertions

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
    """

NumPy Comparison Decorators

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
    """

Test Data Generation

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."""

Test Parameterization

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
    """

Performance Testing

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 Markers and Fixtures

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
    """

Error Handling and Validation

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
    """

Custom Test Utilities

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
    """

Usage Examples

Basic Array Testing

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()

NumPy Comparison Testing

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}")

Parameterized Testing

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}")

Performance Testing and Benchmarking

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()

Custom Kernel Testing

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}")

Error Testing and Edge Cases

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

docs

array-operations.md

cuda-integration.md

fft.md

index.md

indexing-selection.md

input-output.md

jit-kernels.md

linear-algebra.md

logic-operations.md

mathematical-functions.md

random-generation.md

scipy-extensions.md

statistics.md

testing.md

tile.json