or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

array-operations.mdcuda-integration.mdfft.mdindex.mdindexing-selection.mdinput-output.mdjit-kernels.mdlinear-algebra.mdlogic-operations.mdmathematical-functions.mdrandom-generation.mdscipy-extensions.mdstatistics.mdtesting.md

testing.mddocs/

0

# Testing and Debugging Framework

1

2

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.

3

4

## Capabilities

5

6

### Array Assertions

7

8

Assertion functions for validating array properties and correctness in tests.

9

10

```python { .api }

11

def assert_allclose(actual, desired, rtol=1e-7, atol=0, err_msg='', verbose=True):

12

"""Assert arrays are element-wise equal within tolerance.

13

14

Args:

15

actual: Actual array

16

desired: Desired array

17

rtol: Relative tolerance

18

atol: Absolute tolerance

19

err_msg: Error message for assertion failure

20

verbose: Print conflicting values on failure

21

22

Raises:

23

AssertionError: If arrays are not close within tolerance

24

"""

25

26

def assert_array_equal(x, y, err_msg='', verbose=True, strides_check=False):

27

"""Assert arrays are exactly equal.

28

29

Args:

30

x: First input array

31

y: Second input array

32

err_msg: Error message for assertion failure

33

verbose: Print conflicting values on failure

34

strides_check: Check array strides are equal

35

36

Raises:

37

AssertionError: If arrays are not equal

38

"""

39

40

def assert_array_almost_equal(x, y, decimal=6, err_msg='', verbose=True):

41

"""Assert arrays are equal up to specified precision.

42

43

Args:

44

x: First input array

45

y: Second input array

46

decimal: Number of decimal places for comparison

47

err_msg: Error message for assertion failure

48

verbose: Print conflicting values on failure

49

50

Raises:

51

AssertionError: If arrays are not almost equal

52

"""

53

54

def assert_array_less(x, y, err_msg='', verbose=True):

55

"""Assert array elements are less than corresponding elements.

56

57

Args:

58

x: First input array

59

y: Second input array

60

err_msg: Error message for assertion failure

61

verbose: Print conflicting values on failure

62

63

Raises:

64

AssertionError: If condition is not satisfied

65

"""

66

67

def assert_array_list_equal(xlist, ylist, err_msg='', verbose=True):

68

"""Assert lists of arrays are equal.

69

70

Args:

71

xlist: First list of arrays

72

ylist: Second list of arrays

73

err_msg: Error message for assertion failure

74

verbose: Print conflicting values on failure

75

76

Raises:

77

AssertionError: If array lists are not equal

78

"""

79

```

80

81

### NumPy Comparison Decorators

82

83

Decorators for testing CuPy functions against NumPy equivalents with automatic array module switching.

84

85

```python { .api }

86

def numpy_cupy_allclose(rtol=1e-7, atol=0, err_msg='', verbose=True,

87

name='xp', type_check=True, accept_error=False,

88

sp_name=None, scipy_name=None, contiguous_check=True,

89

strides_check=False):

90

"""Decorator for testing CuPy vs NumPy with allclose comparison.

91

92

Args:

93

rtol: Relative tolerance

94

atol: Absolute tolerance

95

err_msg: Error message

96

verbose: Print details on failure

97

name: Parameter name for array module (xp)

98

type_check: Check result types match

99

accept_error: Accept errors as long as both raise same error

100

sp_name: Parameter name for sparse module

101

scipy_name: Parameter name for scipy module

102

contiguous_check: Check array contiguity

103

strides_check: Check array strides

104

105

Example:

106

@numpy_cupy_allclose()

107

def test_function(xp):

108

a = xp.array([1, 2, 3])

109

return xp.sum(a)

110

"""

111

112

def numpy_cupy_array_equal(err_msg='', verbose=True, name='xp',

113

type_check=True, accept_error=False,

114

sp_name=None, scipy_name=None, strides_check=False):

115

"""Decorator for testing CuPy vs NumPy with exact equality.

116

117

Args:

118

err_msg: Error message

119

verbose: Print details on failure

120

name: Parameter name for array module

121

type_check: Check result types match

122

accept_error: Accept matching errors

123

sp_name: Parameter name for sparse module

124

scipy_name: Parameter name for scipy module

125

strides_check: Check array strides

126

127

Example:

128

@numpy_cupy_array_equal()

129

def test_integer_function(xp):

130

a = xp.array([1, 2, 3])

131

return xp.argmax(a)

132

"""

133

134

def numpy_cupy_raises(accept_error=Exception, name='xp', sp_name=None, scipy_name=None):

135

"""Decorator for testing that both NumPy and CuPy raise exceptions.

136

137

Args:

138

accept_error: Expected exception type(s)

139

name: Parameter name for array module

140

sp_name: Parameter name for sparse module

141

scipy_name: Parameter name for scipy module

142

143

Example:

144

@numpy_cupy_raises()

145

def test_invalid_operation(xp):

146

a = xp.array([1, 2, 3])

147

return a / xp.array([0, 1, 2]) # Division by zero

148

"""

149

```

150

151

### Test Data Generation

152

153

Functions for generating test arrays with specific properties and patterns.

154

155

```python { .api }

156

def shaped_arange(shape, xp=None, dtype=float, order='C'):

157

"""Generate array with arange values in specified shape.

158

159

Args:

160

shape: Output array shape

161

xp: Array module (numpy or cupy)

162

dtype: Data type

163

order: Memory layout

164

165

Returns:

166

Array: Shaped arange array

167

168

Example:

169

arr = shaped_arange((2, 3), xp=cp, dtype=int)

170

# Returns: [[0, 1, 2], [3, 4, 5]]

171

"""

172

173

def shaped_random(shape, xp=None, dtype=float, seed=0, scale=10):

174

"""Generate random array with specified properties.

175

176

Args:

177

shape: Output array shape

178

xp: Array module (numpy or cupy)

179

dtype: Data type

180

seed: Random seed for reproducibility

181

scale: Scale factor for random values

182

183

Returns:

184

Array: Random array with specified shape and properties

185

"""

186

187

def shaped_reverse_arange(shape, xp=None, dtype=float):

188

"""Generate array with reverse arange values.

189

190

Args:

191

shape: Output array shape

192

xp: Array module

193

dtype: Data type

194

195

Returns:

196

Array: Reverse arange array

197

"""

198

199

def for_dtypes(dtypes, name='dtype'):

200

"""Parameterize test for multiple data types.

201

202

Args:

203

dtypes: List of data types to test

204

name: Parameter name for dtype

205

206

Returns:

207

Decorator: Parameterized test decorator

208

"""

209

210

def for_all_dtypes(name='dtype', no_float16=False, no_bool=False,

211

no_complex=False, additional_args=()):

212

"""Parameterize test for all standard data types.

213

214

Args:

215

name: Parameter name for dtype

216

no_float16: Exclude float16 type

217

no_bool: Exclude boolean type

218

no_complex: Exclude complex types

219

additional_args: Additional test parameters

220

221

Returns:

222

Decorator: Parameterized test decorator

223

"""

224

225

def for_float_dtypes(name='dtype', no_float16=False, additional_args=()):

226

"""Parameterize test for floating-point data types.

227

228

Args:

229

name: Parameter name for dtype

230

no_float16: Exclude float16 type

231

additional_args: Additional test parameters

232

233

Returns:

234

Decorator: Parameterized test decorator

235

"""

236

237

def for_int_dtypes(name='dtype', no_bool=False, additional_args=()):

238

"""Parameterize test for integer data types.

239

240

Args:

241

name: Parameter name for dtype

242

no_bool: Exclude boolean type

243

additional_args: Additional test parameters

244

245

Returns:

246

Decorator: Parameterized test decorator

247

"""

248

249

def for_signed_dtypes(name='dtype', additional_args=()):

250

"""Parameterize test for signed data types."""

251

252

def for_unsigned_dtypes(name='dtype', additional_args=()):

253

"""Parameterize test for unsigned data types."""

254

255

def for_complex_dtypes(name='dtype', additional_args=()):

256

"""Parameterize test for complex data types."""

257

```

258

259

### Test Parameterization

260

261

Advanced parameterization utilities for comprehensive test coverage.

262

263

```python { .api }

264

def parameterize(*params, **kwargs):

265

"""Parameterize test with multiple parameter combinations.

266

267

Args:

268

*params: Parameter specifications

269

**kwargs: Named parameter specifications

270

271

Returns:

272

Decorator: Parameterized test decorator

273

274

Example:

275

@parameterize([

276

{'shape': (10,), 'axis': None},

277

{'shape': (3, 4), 'axis': 0},

278

{'shape': (2, 3, 4), 'axis': (1, 2)},

279

])

280

def test_reduction(self, shape, axis):

281

...

282

"""

283

284

def product(parameter_dict):

285

"""Generate Cartesian product of parameters.

286

287

Args:

288

parameter_dict: Dictionary of parameter lists

289

290

Returns:

291

Decorator: Cartesian product test decorator

292

293

Example:

294

@product({

295

'shape': [(10,), (3, 4), (2, 3, 4)],

296

'dtype': [float32, float64],

297

'axis': [None, 0, -1]

298

})

299

def test_function(self, shape, dtype, axis):

300

...

301

"""

302

303

def product_dict(*dicts):

304

"""Generate Cartesian product from multiple dictionaries.

305

306

Args:

307

*dicts: Parameter dictionaries to combine

308

309

Returns:

310

Decorator: Combined parameter test decorator

311

"""

312

```

313

314

### Performance Testing

315

316

Utilities for benchmarking and performance validation.

317

318

```python { .api }

319

def time_cupy_vs_numpy(func, args=(), number=1, repeat=3, setup='pass'):

320

"""Compare execution time between CuPy and NumPy.

321

322

Args:

323

func: Function to benchmark

324

args: Function arguments

325

number: Number of executions per timing

326

repeat: Number of timing repetitions

327

setup: Setup code

328

329

Returns:

330

dict: Timing results for both libraries

331

"""

332

333

class Timer:

334

"""High-resolution timer for performance measurements.

335

336

Example:

337

with Timer() as timer:

338

result = expensive_operation()

339

print(f"Operation took {timer.elapsed:.3f} seconds")

340

"""

341

342

def __enter__(self):

343

self.start_time = time.perf_counter()

344

return self

345

346

def __exit__(self, exc_type, exc_val, exc_tb):

347

self.end_time = time.perf_counter()

348

self.elapsed = self.end_time - self.start_time

349

350

def benchmark_function(func, args=(), warmup=3, number=10):

351

"""Benchmark function execution time.

352

353

Args:

354

func: Function to benchmark

355

args: Function arguments

356

warmup: Number of warmup runs

357

number: Number of timing runs

358

359

Returns:

360

dict: Benchmark statistics (mean, std, min, max)

361

"""

362

```

363

364

### Test Markers and Fixtures

365

366

Test execution control and resource management utilities.

367

368

```python { .api }

369

def gpu(*args, **kwargs):

370

"""Mark test as requiring GPU.

371

372

Args:

373

*args: GPU requirements

374

**kwargs: Additional GPU parameters

375

376

Returns:

377

Decorator: GPU test marker

378

"""

379

380

def multi_gpu(gpu_num):

381

"""Mark test as requiring multiple GPUs.

382

383

Args:

384

gpu_num: Number of GPUs required

385

386

Returns:

387

Decorator: Multi-GPU test marker

388

"""

389

390

def slow():

391

"""Mark test as slow-running.

392

393

Returns:

394

Decorator: Slow test marker

395

"""

396

397

def condition(cond):

398

"""Conditional test execution.

399

400

Args:

401

cond: Boolean condition for test execution

402

403

Returns:

404

Decorator: Conditional test decorator

405

"""

406

407

def repeat(n):

408

"""Repeat test execution n times.

409

410

Args:

411

n: Number of repetitions

412

413

Returns:

414

Decorator: Test repetition decorator

415

"""

416

```

417

418

### Error Handling and Validation

419

420

Utilities for testing error conditions and input validation.

421

422

```python { .api }

423

def raises(exception_type, match=None):

424

"""Context manager for testing exceptions.

425

426

Args:

427

exception_type: Expected exception type

428

match: Regex pattern for exception message

429

430

Returns:

431

Context manager for exception testing

432

433

Example:

434

with raises(ValueError, match="invalid shape"):

435

cp.array([1, 2, 3]).reshape((2, 2))

436

"""

437

438

def warns(warning_type, match=None):

439

"""Context manager for testing warnings.

440

441

Args:

442

warning_type: Expected warning type

443

match: Regex pattern for warning message

444

445

Returns:

446

Context manager for warning testing

447

"""

448

449

def assert_warns(warning_class, func, *args, **kwargs):

450

"""Assert function raises specific warning.

451

452

Args:

453

warning_class: Expected warning type

454

func: Function to test

455

*args: Function arguments

456

**kwargs: Function keyword arguments

457

458

Returns:

459

Function result if warning is raised

460

"""

461

462

def suppress_warnings():

463

"""Context manager for suppressing warnings during tests.

464

465

Returns:

466

Context manager for warning suppression

467

"""

468

```

469

470

### Custom Test Utilities

471

472

Specialized utilities for CuPy-specific testing scenarios.

473

474

```python { .api }

475

def assert_array_equal_ex(x, y, strides_check=False):

476

"""Extended array equality with stride checking.

477

478

Args:

479

x: First array

480

y: Second array

481

strides_check: Check stride equality

482

483

Raises:

484

AssertionError: If arrays differ

485

"""

486

487

def generate_matrix(shape, dtype, matrix_type='random', seed=None):

488

"""Generate test matrix with specific properties.

489

490

Args:

491

shape: Matrix shape

492

dtype: Data type

493

matrix_type: Matrix type ('random', 'identity', 'zeros', 'ones', 'diagonal')

494

seed: Random seed

495

496

Returns:

497

cupy.ndarray: Generated test matrix

498

"""

499

500

def check_elementwise_kernel(kernel_func, numpy_func, test_cases, rtol=1e-7, atol=0):

501

"""Validate custom elementwise kernel against NumPy function.

502

503

Args:

504

kernel_func: CuPy kernel function

505

numpy_func: Reference NumPy function

506

test_cases: List of test input arrays

507

rtol: Relative tolerance

508

atol: Absolute tolerance

509

510

Raises:

511

AssertionError: If kernel results differ from NumPy

512

"""

513

514

def check_reduction_kernel(kernel_func, numpy_func, test_cases, axes_list=None):

515

"""Validate custom reduction kernel against NumPy function.

516

517

Args:

518

kernel_func: CuPy reduction kernel

519

numpy_func: Reference NumPy function

520

test_cases: List of test input arrays

521

axes_list: List of axes to test

522

523

Raises:

524

AssertionError: If kernel results differ from NumPy

525

"""

526

```

527

528

## Usage Examples

529

530

### Basic Array Testing

531

532

```python

533

import cupy as cp

534

import numpy as np

535

from cupy.testing import assert_allclose, assert_array_equal

536

537

# Test basic array operations

538

def test_array_creation():

539

"""Test array creation functions."""

540

# Test zeros

541

cupy_zeros = cp.zeros((3, 4), dtype=cp.float32)

542

numpy_zeros = np.zeros((3, 4), dtype=np.float32)

543

544

assert_array_equal(cupy_zeros, numpy_zeros)

545

print("✓ Array creation test passed")

546

547

def test_mathematical_operations():

548

"""Test mathematical operations."""

549

# Create test data

550

np.random.seed(42)

551

data_np = np.random.randn(100, 50).astype(np.float32)

552

data_cp = cp.asarray(data_np)

553

554

# Test sum operation

555

sum_np = np.sum(data_np, axis=1)

556

sum_cp = cp.sum(data_cp, axis=1)

557

558

assert_allclose(sum_cp, sum_np, rtol=1e-6)

559

print("✓ Sum operation test passed")

560

561

# Test complex operation

562

result_np = np.sqrt(np.sum(data_np**2, axis=1))

563

result_cp = cp.sqrt(cp.sum(data_cp**2, axis=1))

564

565

assert_allclose(result_cp, result_np, rtol=1e-6)

566

print("✓ Complex operation test passed")

567

568

# Run basic tests

569

test_array_creation()

570

test_mathematical_operations()

571

```

572

573

### NumPy Comparison Testing

574

575

```python

576

import cupy as cp

577

from cupy.testing import numpy_cupy_allclose, numpy_cupy_array_equal, shaped_arange

578

579

class TestMathFunctions:

580

"""Test mathematical functions using comparison decorators."""

581

582

@numpy_cupy_allclose(rtol=1e-6)

583

def test_trigonometric_functions(self, xp):

584

"""Test trigonometric functions."""

585

x = shaped_arange((100,), xp, dtype=xp.float32)

586

x = x / 10 # Scale to reasonable range

587

588

# Test multiple trig functions

589

sin_result = xp.sin(x)

590

cos_result = xp.cos(x)

591

combined = sin_result**2 + cos_result**2 # Should be close to 1

592

593

return combined

594

595

@numpy_cupy_allclose(rtol=1e-5)

596

def test_reduction_operations(self, xp):

597

"""Test reduction operations."""

598

data = shaped_arange((50, 40), xp, dtype=xp.float64)

599

600

# Test various reductions

601

results = {

602

'sum_all': xp.sum(data),

603

'sum_axis0': xp.sum(data, axis=0),

604

'sum_axis1': xp.sum(data, axis=1),

605

'mean_all': xp.mean(data),

606

'std_all': xp.std(data),

607

}

608

609

return results

610

611

@numpy_cupy_array_equal()

612

def test_indexing_operations(self, xp):

613

"""Test indexing operations (exact equality expected)."""

614

data = shaped_arange((20, 30), xp, dtype=xp.int32)

615

616

# Test various indexing patterns

617

results = {

618

'slice1': data[5:15, 10:25],

619

'stride': data[::2, ::3],

620

'boolean': data[data > 200],

621

'fancy': data[[1, 3, 5], [2, 4, 6]],

622

}

623

624

return results

625

626

# Run comparison tests

627

test_class = TestMathFunctions()

628

629

print("Running NumPy comparison tests...")

630

631

try:

632

result = test_class.test_trigonometric_functions()

633

print("✓ Trigonometric functions test passed")

634

except Exception as e:

635

print(f"✗ Trigonometric functions test failed: {e}")

636

637

try:

638

result = test_class.test_reduction_operations()

639

print("✓ Reduction operations test passed")

640

except Exception as e:

641

print(f"✗ Reduction operations test failed: {e}")

642

643

try:

644

result = test_class.test_indexing_operations()

645

print("✓ Indexing operations test passed")

646

except Exception as e:

647

print(f"✗ Indexing operations test failed: {e}")

648

```

649

650

### Parameterized Testing

651

652

```python

653

import cupy as cp

654

from cupy.testing import for_all_dtypes, for_float_dtypes, parameterize, shaped_arange

655

656

class TestParameterizedFunctions:

657

"""Test functions with multiple parameter combinations."""

658

659

@for_all_dtypes(no_complex=True)

660

def test_array_creation_dtypes(self, dtype):

661

"""Test array creation for all data types."""

662

# Create array with specific dtype

663

arr = cp.zeros(100, dtype=dtype)

664

665

# Verify dtype

666

assert arr.dtype == dtype

667

668

# Verify values

669

expected = cp.zeros(100, dtype=dtype)

670

cp.testing.assert_array_equal(arr, expected)

671

672

print(f"✓ Array creation test passed for dtype: {dtype}")

673

674

@for_float_dtypes()

675

def test_mathematical_precision(self, dtype):

676

"""Test mathematical operations for floating-point types."""

677

# Generate test data

678

data = shaped_arange((50,), dtype=dtype) / 10

679

680

# Test operation that requires precision

681

result = cp.exp(cp.log(data + 1)) # Should equal data + 1

682

expected = data + 1

683

684

# Adjust tolerance based on dtype precision

685

if dtype == cp.float16:

686

rtol = 1e-3

687

elif dtype == cp.float32:

688

rtol = 1e-6

689

else: # float64

690

rtol = 1e-12

691

692

cp.testing.assert_allclose(result, expected, rtol=rtol)

693

print(f"✓ Mathematical precision test passed for dtype: {dtype}")

694

695

@parameterize([

696

{'shape': (100,), 'axis': None},

697

{'shape': (10, 10), 'axis': 0},

698

{'shape': (10, 10), 'axis': 1},

699

{'shape': (5, 4, 3), 'axis': (0, 2)},

700

{'shape': (2, 3, 4, 5), 'axis': None},

701

])

702

def test_sum_various_shapes(self, shape, axis):

703

"""Test sum operation for various shapes and axes."""

704

# Create test array

705

arr = shaped_arange(shape, cp, dtype=cp.float32)

706

707

# Compute sum

708

result = cp.sum(arr, axis=axis)

709

710

# Verify result properties

711

if axis is None:

712

assert result.ndim == 0 # Scalar result

713

else:

714

# Check output shape

715

expected_shape = list(shape)

716

if isinstance(axis, int):

717

expected_shape.pop(axis)

718

else:

719

for ax in sorted(axis, reverse=True):

720

expected_shape.pop(ax)

721

expected_shape = tuple(expected_shape) if expected_shape else ()

722

723

assert result.shape == expected_shape

724

725

print(f"✓ Sum test passed for shape: {shape}, axis: {axis}")

726

727

# Run parameterized tests

728

test_class = TestParameterizedFunctions()

729

730

# Test all data types

731

print("Testing array creation for all data types:")

732

for dtype in [cp.int8, cp.int16, cp.int32, cp.int64,

733

cp.float16, cp.float32, cp.float64, cp.bool_]:

734

try:

735

test_class.test_array_creation_dtypes(dtype)

736

except Exception as e:

737

print(f"✗ Failed for dtype {dtype}: {e}")

738

739

# Test floating-point precision

740

print("\nTesting mathematical precision for float types:")

741

for dtype in [cp.float16, cp.float32, cp.float64]:

742

try:

743

test_class.test_mathematical_precision(dtype)

744

except Exception as e:

745

print(f"✗ Failed for dtype {dtype}: {e}")

746

747

# Test various shapes manually (simulating parameterized test)

748

print("\nTesting sum operation for various shapes:")

749

test_params = [

750

{'shape': (100,), 'axis': None},

751

{'shape': (10, 10), 'axis': 0},

752

{'shape': (10, 10), 'axis': 1},

753

{'shape': (5, 4, 3), 'axis': (0, 2)},

754

{'shape': (2, 3, 4, 5), 'axis': None},

755

]

756

757

for params in test_params:

758

try:

759

test_class.test_sum_various_shapes(**params)

760

except Exception as e:

761

print(f"✗ Failed for params {params}: {e}")

762

```

763

764

### Performance Testing and Benchmarking

765

766

```python

767

import cupy as cp

768

import numpy as np

769

import time

770

771

class PerformanceTester:

772

"""Performance testing utilities."""

773

774

def __init__(self):

775

self.warmup_runs = 3

776

self.timing_runs = 10

777

778

def benchmark_function(self, func, args=(), warmup=None, runs=None):

779

"""Benchmark function execution time."""

780

warmup = warmup or self.warmup_runs

781

runs = runs or self.timing_runs

782

783

# Warmup runs

784

for _ in range(warmup):

785

result = func(*args)

786

if hasattr(cp, 'cuda'):

787

cp.cuda.Stream.null.synchronize()

788

789

# Timing runs

790

times = []

791

for _ in range(runs):

792

start_time = time.perf_counter()

793

result = func(*args)

794

if hasattr(cp, 'cuda'):

795

cp.cuda.Stream.null.synchronize()

796

end_time = time.perf_counter()

797

times.append(end_time - start_time)

798

799

return {

800

'mean': np.mean(times),

801

'std': np.std(times),

802

'min': np.min(times),

803

'max': np.max(times),

804

'times': times,

805

'result': result

806

}

807

808

def compare_cupy_numpy(self, operation_name, cupy_func, numpy_func,

809

cupy_args=(), numpy_args=None):

810

"""Compare CuPy and NumPy performance."""

811

numpy_args = numpy_args or cupy_args

812

813

print(f"\nBenchmarking {operation_name}:")

814

815

# Benchmark NumPy

816

numpy_stats = self.benchmark_function(numpy_func, numpy_args)

817

818

# Benchmark CuPy

819

cupy_stats = self.benchmark_function(cupy_func, cupy_args)

820

821

# Calculate speedup

822

speedup = numpy_stats['mean'] / cupy_stats['mean']

823

824

print(f" NumPy: {numpy_stats['mean']*1000:6.2f} ± {numpy_stats['std']*1000:5.2f} ms")

825

print(f" CuPy: {cupy_stats['mean']*1000:6.2f} ± {cupy_stats['std']*1000:5.2f} ms")

826

print(f" Speedup: {speedup:.2f}x")

827

828

return {

829

'numpy': numpy_stats,

830

'cupy': cupy_stats,

831

'speedup': speedup

832

}

833

834

# Performance testing examples

835

tester = PerformanceTester()

836

837

# Large matrix operations

838

n = 5000

839

print(f"Performance testing with matrices of size {n}x{n}")

840

841

# Matrix multiplication

842

A_np = np.random.randn(n, n).astype(np.float32)

843

B_np = np.random.randn(n, n).astype(np.float32)

844

A_cp = cp.asarray(A_np)

845

B_cp = cp.asarray(B_np)

846

847

results = tester.compare_cupy_numpy(

848

"Matrix Multiplication",

849

lambda: cp.dot(A_cp, B_cp),

850

lambda: np.dot(A_np, B_np)

851

)

852

853

# Element-wise operations

854

large_array_np = np.random.randn(10_000_000).astype(np.float32)

855

large_array_cp = cp.asarray(large_array_np)

856

857

results = tester.compare_cupy_numpy(

858

"Element-wise Sin",

859

lambda: cp.sin(large_array_cp),

860

lambda: np.sin(large_array_np)

861

)

862

863

results = tester.compare_cupy_numpy(

864

"Array Sum",

865

lambda: cp.sum(large_array_cp),

866

lambda: np.sum(large_array_np)

867

)

868

869

# FFT operations

870

fft_data_np = np.random.randn(2**20).astype(np.complex64)

871

fft_data_cp = cp.asarray(fft_data_np)

872

873

results = tester.compare_cupy_numpy(

874

"FFT",

875

lambda: cp.fft.fft(fft_data_cp),

876

lambda: np.fft.fft(fft_data_np)

877

)

878

879

# Memory bandwidth test

880

def memory_bandwidth_test():

881

"""Test memory bandwidth with various operations."""

882

sizes = [2**i for i in range(20, 28)] # From 1MB to 1GB

883

884

print("\nMemory Bandwidth Test:")

885

print("Size (MB) Copy (GB/s) Add (GB/s)")

886

887

for size in sizes:

888

n_elements = size

889

n_bytes = n_elements * 4 # float32

890

size_mb = n_bytes / (1024**2)

891

892

if size_mb > 500: # Skip very large sizes

893

break

894

895

# Generate data

896

a = cp.random.randn(n_elements).astype(cp.float32)

897

b = cp.random.randn(n_elements).astype(cp.float32)

898

899

# Test copy bandwidth

900

copy_stats = tester.benchmark_function(lambda: cp.copy(a), runs=5)

901

copy_bandwidth = (n_bytes / copy_stats['mean']) / (1024**3)

902

903

# Test add bandwidth (read a, read b, write result)

904

add_stats = tester.benchmark_function(lambda: a + b, runs=5)

905

add_bandwidth = (3 * n_bytes / add_stats['mean']) / (1024**3)

906

907

print(f"{size_mb:8.1f} {copy_bandwidth:8.1f} {add_bandwidth:8.1f}")

908

909

memory_bandwidth_test()

910

```

911

912

### Custom Kernel Testing

913

914

```python

915

import cupy as cp

916

from cupy import ElementwiseKernel, ReductionKernel

917

918

class CustomKernelTester:

919

"""Test custom CuPy kernels against reference implementations."""

920

921

def test_elementwise_kernel(self):

922

"""Test custom elementwise kernel."""

923

# Define custom kernel

924

square_diff_kernel = ElementwiseKernel(

925

'float32 x, float32 y',

926

'float32 z',

927

'z = (x - y) * (x - y)',

928

'square_diff'

929

)

930

931

# Generate test data

932

n = 10000

933

x = cp.random.randn(n).astype(cp.float32)

934

y = cp.random.randn(n).astype(cp.float32)

935

936

# Test kernel

937

kernel_result = square_diff_kernel(x, y)

938

939

# Reference implementation

940

reference_result = (x - y) ** 2

941

942

# Validate

943

cp.testing.assert_allclose(kernel_result, reference_result, rtol=1e-6)

944

print("✓ Elementwise kernel test passed")

945

946

return kernel_result

947

948

def test_reduction_kernel(self):

949

"""Test custom reduction kernel."""

950

# Define sum of squares kernel

951

sum_of_squares_kernel = ReductionKernel(

952

'float32 x',

953

'float32 y',

954

'x * x', # map expression

955

'a + b', # reduce expression

956

'y = a', # post expression

957

'0', # identity

958

'sum_of_squares'

959

)

960

961

# Generate test data

962

n = 10000

963

x = cp.random.randn(n).astype(cp.float32)

964

965

# Test kernel

966

kernel_result = sum_of_squares_kernel(x)

967

968

# Reference implementation

969

reference_result = cp.sum(x * x)

970

971

# Validate

972

cp.testing.assert_allclose(kernel_result, reference_result, rtol=1e-6)

973

print("✓ Reduction kernel test passed")

974

975

return kernel_result

976

977

def test_complex_kernel_workflow(self):

978

"""Test complex workflow combining multiple kernels."""

979

# Step 1: Normalize data

980

normalize_kernel = ElementwiseKernel(

981

'float32 x, float32 mean, float32 std',

982

'float32 z',

983

'z = (x - mean) / std',

984

'normalize'

985

)

986

987

# Step 2: Compute variance after normalization

988

variance_kernel = ReductionKernel(

989

'float32 x',

990

'float32 var',

991

'x * x',

992

'a + b',

993

'var = a / _in_ind.size()',

994

'0',

995

'variance'

996

)

997

998

# Generate test data

999

n = 50000

1000

data = cp.random.normal(10, 3, n).astype(cp.float32)

1001

1002

# Compute statistics

1003

mean_val = cp.mean(data)

1004

std_val = cp.std(data)

1005

1006

# Apply normalization kernel

1007

normalized = normalize_kernel(data, mean_val, std_val)

1008

1009

# Compute variance of normalized data (should be ~1)

1010

variance = variance_kernel(normalized)

1011

1012

print(f"Original data: mean={float(mean_val):.3f}, std={float(std_val):.3f}")

1013

print(f"Normalized data: mean={float(cp.mean(normalized)):.6f}, variance={float(variance):.6f}")

1014

1015

# Validate normalization

1016

assert abs(float(cp.mean(normalized))) < 1e-6, "Mean should be ~0"

1017

assert abs(float(variance) - 1.0) < 1e-3, "Variance should be ~1"

1018

1019

print("✓ Complex kernel workflow test passed")

1020

1021

return normalized, variance

1022

1023

# Run custom kernel tests

1024

tester = CustomKernelTester()

1025

1026

try:

1027

tester.test_elementwise_kernel()

1028

tester.test_reduction_kernel()

1029

tester.test_complex_kernel_workflow()

1030

print("\n✓ All custom kernel tests passed!")

1031

except Exception as e:

1032

print(f"\n✗ Custom kernel test failed: {e}")

1033

```

1034

1035

### Error Testing and Edge Cases

1036

1037

```python

1038

import cupy as cp

1039

from cupy.testing import assert_array_equal

1040

import pytest

1041

1042

class ErrorTester:

1043

"""Test error conditions and edge cases."""

1044

1045

def test_division_by_zero(self):

1046

"""Test division by zero handling."""

1047

# Test with different settings

1048

a = cp.array([1.0, 2.0, 3.0])

1049

b = cp.array([1.0, 0.0, 2.0])

1050

1051

# Should produce inf without error

1052

result = a / b

1053

expected = cp.array([1.0, cp.inf, 1.5])

1054

1055

cp.testing.assert_array_equal(result, expected)

1056

print("✓ Division by zero test passed")

1057

1058

def test_invalid_shapes(self):

1059

"""Test operations with invalid shapes."""

1060

a = cp.array([[1, 2], [3, 4]])

1061

b = cp.array([1, 2, 3])

1062

1063

# This should raise an error

1064

try:

1065

result = a @ b # Invalid matrix multiplication

1066

assert False, "Expected ValueError for invalid shapes"

1067

except ValueError:

1068

print("✓ Invalid shape error test passed")

1069

1070

def test_out_of_bounds_indexing(self):

1071

"""Test out-of-bounds indexing."""

1072

arr = cp.arange(10)

1073

1074

# This should raise IndexError

1075

try:

1076

value = arr[15]

1077

assert False, "Expected IndexError"

1078

except IndexError:

1079

print("✓ Out-of-bounds indexing test passed")

1080

1081

def test_dtype_overflow(self):

1082

"""Test data type overflow conditions."""

1083

# Test integer overflow

1084

a = cp.array([127], dtype=cp.int8)

1085

b = cp.array([1], dtype=cp.int8)

1086

1087

result = a + b # Should overflow

1088

# Note: behavior may be platform dependent

1089

print(f"Int8 overflow result: {result} (expected: -128 or wrapped value)")

1090

1091

# Test with larger types

1092

large_val = cp.array([2**31 - 1], dtype=cp.int32)

1093

overflow_result = large_val + cp.array([1], dtype=cp.int32)

1094

print(f"Int32 overflow: {large_val} + 1 = {overflow_result}")

1095

1096

print("✓ Dtype overflow test completed")

1097

1098

def test_nan_propagation(self):

1099

"""Test NaN propagation in calculations."""

1100

data = cp.array([1.0, cp.nan, 3.0, 4.0])

1101

1102

# Test various operations

1103

sum_result = cp.sum(data) # Should be NaN

1104

nansum_result = cp.nansum(data) # Should ignore NaN

1105

1106

assert cp.isnan(sum_result), "Sum should propagate NaN"

1107

assert not cp.isnan(nansum_result), "nansum should ignore NaN"

1108

assert cp.isclose(nansum_result, 8.0), f"nansum should equal 8, got {nansum_result}"

1109

1110

print("✓ NaN propagation test passed")

1111

1112

def test_memory_limits(self):

1113

"""Test behavior near memory limits."""

1114

# Try to allocate very large array

1115

try:

1116

# This will likely fail on most systems

1117

huge_array = cp.zeros((100000, 100000), dtype=cp.float64)

1118

print(f"Successfully allocated huge array: {huge_array.shape}")

1119

except cp.cuda.memory.OutOfMemoryError:

1120

print("✓ Out of memory error handled correctly")

1121

except MemoryError:

1122

print("✓ Memory error handled correctly")

1123

1124

# Test gradual memory allocation

1125

arrays = []

1126

try:

1127

for i in range(100):

1128

arr = cp.random.randn(1000, 1000)

1129

arrays.append(arr)

1130

if i % 20 == 0:

1131

print(f"Allocated {i+1} arrays, total memory: ~{(i+1)*8:.1f} MB")

1132

except (cp.cuda.memory.OutOfMemoryError, MemoryError):

1133

print(f"Memory limit reached after {len(arrays)} arrays")

1134

1135

# Clean up

1136

del arrays

1137

cp.get_default_memory_pool().free_all_blocks()

1138

print("✓ Memory cleanup completed")

1139

1140

# Run error tests

1141

error_tester = ErrorTester()

1142

1143

print("Running error and edge case tests:")

1144

1145

try:

1146

error_tester.test_division_by_zero()

1147

error_tester.test_invalid_shapes()

1148

error_tester.test_out_of_bounds_indexing()

1149

error_tester.test_dtype_overflow()

1150

error_tester.test_nan_propagation()

1151

error_tester.test_memory_limits()

1152

1153

print("\n✓ All error handling tests completed successfully!")

1154

1155

except Exception as e:

1156

print(f"\n✗ Error in error testing: {e}")

1157

```