Coverage for openhcs/processing/backends/lib_registry/cupy_registry.py: 63.3%
49 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 02:09 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 02:09 +0000
1"""
2Clean CuPy Registry Implementation
4Implements clean abstraction with internal library-specific logic.
5All CuPy-specific details (GPU handling, CuCIM integration, etc.)
6are handled internally without leaking into the ABC.
7"""
8from __future__ import annotations
10import numpy as np
11from typing import Tuple, List
13from openhcs.constants import MemoryType
14from openhcs.core.utils import optional_import
15from .unified_registry import LibraryRegistryBase, RuntimeTestingRegistryBase
17cp = optional_import("cupy")
18cucim = optional_import("cucim")
19cucim_skimage = optional_import("cucim.skimage")
22class CupyRegistry(RuntimeTestingRegistryBase):
23 """Clean CuPy registry with internal GPU handling logic."""
25 # Registry name for auto-registration
26 _registry_name = 'cupy'
28 # Library-specific exclusions (uses common ones)
29 EXCLUSIONS = LibraryRegistryBase.COMMON_EXCLUSIONS
31 # Modules to scan for functions
32 MODULES_TO_SCAN = ['filters', 'morphology', 'measure', 'segmentation',
33 'feature', 'restoration', 'transform', 'exposure',
34 'color', 'util']
36 # Memory type for this registry
37 MEMORY_TYPE = MemoryType.CUPY.value
39 # Float dtype for this registry
40 FLOAT_DTYPE = cp.float32
42 def __init__(self):
43 super().__init__("cupy")
45 # ===== ESSENTIAL ABC METHODS =====
46 def get_library_version(self) -> str:
47 return cucim.__version__
49 def is_library_available(self) -> bool:
50 return bool(cp) and bool(cucim_skimage)
52 def get_library_object(self):
53 return cucim_skimage
55 def get_module_patterns(self) -> List[str]:
56 """Get module patterns for CuPy (includes cucim patterns)."""
57 return ['cupy', 'cucim']
59 def get_display_name(self) -> str:
60 """Get proper display name for CuPy."""
61 return 'CuPy'
63 # ===== HOOK IMPLEMENTATIONS =====
64 def _create_array(self, shape: Tuple[int, ...], dtype):
65 try:
66 return cp.random.rand(*shape).astype(dtype)
67 except Exception as e:
68 # If CUDA initialization fails, raise a more descriptive error
69 raise RuntimeError(f"CUDA initialization failed during CuPy array creation: {e}") from e
71 def _check_first_parameter(self, first_param, func_name: str) -> bool:
72 return first_param.name.lower() in {'image', 'input', 'array', 'img'}
74 def _preprocess_input(self, image, func_name: str):
75 return image # No preprocessing needed for CuPy
77 def _postprocess_output(self, result, original_image, func_name: str):
78 # ProcessingContract system handles dimensional behavior - no categorization needed
79 return result
81 # ===== LIBRARY-SPECIFIC IMPLEMENTATIONS =====
82 def _generate_function_name(self, name: str, module_name: str) -> str:
83 """Generate function name - original for filters, prefixed for others."""
84 return name if module_name == 'filters' else f"{module_name}_{name}"
86 def _generate_tags(self, func_name: str) -> List[str]:
87 """Generate tags with GPU tag."""
88 tags = func_name.lower().replace("_", " ").split()
89 tags.append("gpu")
90 return tags
92 def _stack_2d_results(self, func, test_3d):
93 """Stack 2D results using CuPy."""
94 results = [func(test_3d[z]) for z in range(test_3d.shape[0])]
95 return cp.stack(results)
97 def _arrays_close(self, arr1, arr2):
98 """Compare arrays using CuPy."""
99 return np.allclose(arr1.get(), arr2.get(), rtol=1e-5, atol=1e-8)