pyfector.backend
Array backend abstraction: seamless NumPy / CuPy switching.
Usage: from pyfector.backend import get_backend, set_device set_device("gpu") # or "cpu" (default) xp = get_backend() # returns numpy or cupy module A = xp.random.randn(100, 50)
All pyfector internals call get_backend() to obtain the active array
module, so every matrix operation automatically runs on CPU or GPU.
1""" 2Array backend abstraction: seamless NumPy / CuPy switching. 3 4Usage: 5 from pyfector.backend import get_backend, set_device 6 set_device("gpu") # or "cpu" (default) 7 xp = get_backend() # returns numpy or cupy module 8 A = xp.random.randn(100, 50) 9 10All pyfector internals call ``get_backend()`` to obtain the active array 11module, so every matrix operation automatically runs on CPU or GPU. 12""" 13 14from __future__ import annotations 15 16import numpy as np 17from typing import Literal 18 19_DEVICE: Literal["cpu", "gpu"] = "cpu" 20_CUPY_AVAILABLE: bool | None = None 21 22 23def _check_cupy() -> bool: 24 global _CUPY_AVAILABLE 25 if _CUPY_AVAILABLE is None: 26 try: 27 import cupy # noqa: F401 28 _CUPY_AVAILABLE = True 29 except ImportError: 30 _CUPY_AVAILABLE = False 31 return _CUPY_AVAILABLE 32 33 34def set_device(device: Literal["cpu", "gpu"]) -> None: 35 """Set the compute device globally.""" 36 global _DEVICE 37 if device == "gpu" and not _check_cupy(): 38 raise ImportError( 39 "CuPy is required for GPU support. Install it with: " 40 "pip install cupy-cuda12x (adjust for your CUDA version)" 41 ) 42 _DEVICE = device 43 44 45def get_device() -> Literal["cpu", "gpu"]: 46 """Return the current device.""" 47 return _DEVICE 48 49 50def get_backend(): 51 """Return the active array module (numpy or cupy).""" 52 if _DEVICE == "gpu": 53 import cupy 54 return cupy 55 return np 56 57 58def to_numpy(arr) -> np.ndarray: 59 """Ensure *arr* is a host numpy array (move from GPU if needed).""" 60 if _DEVICE == "gpu": 61 import cupy 62 if isinstance(arr, cupy.ndarray): 63 return cupy.asnumpy(arr) 64 return np.asarray(arr) 65 66 67def to_device(arr): 68 """Move a numpy array to the current device.""" 69 if _DEVICE == "gpu": 70 import cupy 71 return cupy.asarray(arr) 72 return np.asarray(arr) 73 74 75def make_rng(seed: int | None = None): 76 """Create a seeded host RNG. 77 78 Cross-validation and inference use this generator only for indices 79 and seeds, so keeping it on the host avoids backend-specific RNG 80 differences while preserving reproducibility. 81 """ 82 if seed is not None: 83 return np.random.default_rng(seed) 84 return np.random.default_rng()
35def set_device(device: Literal["cpu", "gpu"]) -> None: 36 """Set the compute device globally.""" 37 global _DEVICE 38 if device == "gpu" and not _check_cupy(): 39 raise ImportError( 40 "CuPy is required for GPU support. Install it with: " 41 "pip install cupy-cuda12x (adjust for your CUDA version)" 42 ) 43 _DEVICE = device
Set the compute device globally.
Return the current device.
51def get_backend(): 52 """Return the active array module (numpy or cupy).""" 53 if _DEVICE == "gpu": 54 import cupy 55 return cupy 56 return np
Return the active array module (numpy or cupy).
59def to_numpy(arr) -> np.ndarray: 60 """Ensure *arr* is a host numpy array (move from GPU if needed).""" 61 if _DEVICE == "gpu": 62 import cupy 63 if isinstance(arr, cupy.ndarray): 64 return cupy.asnumpy(arr) 65 return np.asarray(arr)
Ensure arr is a host numpy array (move from GPU if needed).
68def to_device(arr): 69 """Move a numpy array to the current device.""" 70 if _DEVICE == "gpu": 71 import cupy 72 return cupy.asarray(arr) 73 return np.asarray(arr)
Move a numpy array to the current device.
76def make_rng(seed: int | None = None): 77 """Create a seeded host RNG. 78 79 Cross-validation and inference use this generator only for indices 80 and seeds, so keeping it on the host avoids backend-specific RNG 81 differences while preserving reproducibility. 82 """ 83 if seed is not None: 84 return np.random.default_rng(seed) 85 return np.random.default_rng()
Create a seeded host RNG.
Cross-validation and inference use this generator only for indices and seeds, so keeping it on the host avoids backend-specific RNG differences while preserving reproducibility.