I am trying to make a numpy/cupy interchange script, similar to this backend implementation. Such that by using something like from Util.Backend import backend as bd
, I can create bd.array()
that can switch between numpy and cupy. But I had a lot of trouble doing so, especially about creating array.
After some experiments, it seems that an assignment in the form of:
import cupy as cp
A = cp.array([1, 2, 3])
B = cp.array([A[0], A[1], 3])
Will result in error:
TypeError: Implicit conversion to a NumPy array is not allowed. Please use `.get()` to construct a NumPy array explicitly.
However, if the same array creation is written as:
B = cp.array([A[0], A[1], A[2]])
Then it becomes totally fine (which is also weird, since I did not import numpy at all in the example above, it's almost like []
is being created as a numpy array first).
Similarly,
s = 2
c = 3
one = 1.0
B = cp.array([s, c, one])
Is fine. But if some of the entries are not created in the same way, such as:
s = cp.sin(2)
c = cp.cos(1)
one = 1.0
B = cp.array([s, c, one])
Then the TypeError
would come in again. Note that if numpy is used instead of cupy, none of the above array creation would have raised an error.
This post seem to indicate that cupy does not support a mix type data. But by the suggested method I would have to write CParray.get()
to make the conversion, it is a cupy method and not a numpy one, thus will create new errors if that backend module is running numpy.
Would it be possible to find a way to write array creations so that cp
and np
are interchangeable?
Problem is CuPy and NumPy handle implicit conversions differently. CuPy does not allow implicit conversion between NumPy arrays, Python scalars, or CuPy arrays when creating arrays, while NumPy is more permissive.
In solution you must do 1- Explicit type casting 2- Create a wrapper function that ensures all elements are compatible with the backend (numpy or cupy). This function can preprocess inputs to avoid issues.
import numpy as np
import cupy as cp
class Backend:
def __init__(self, use_cupy=False):
self.backend = cp if use_cupy else np
def array(self, data):
# Convert all elements to the backend-compatible type
backend = self.backend
if backend == cp:
# Ensure compatibility with CuPy
data = [self._to_cupy_element(x) for x in data]
return backend.array(data)
def sin(self, x):
return self.backend.sin(x)
def cos(self, x):
return self.backend.cos(x)
def _to_cupy_element(self, x):
# Ensure elements are CuPy-compatible
if isinstance(x, (int, float, bool)): # Python scalars
return x
elif isinstance(x, np.ndarray): # NumPy array
return cp.asarray(x)
elif isinstance(x, cp.ndarray): # CuPy array
return x
else:
raise TypeError(f"Unsupported type for CuPy: {type(x)}")
# Example Usage:
use_cupy = True # Set to False to use NumPy
backend = Backend(use_cupy=use_cupy)
# Example 1: Create an array
s = backend.sin(2)
c = backend.cos(1)
one = 1.0
B = backend.array([s, c, one]) # Works seamlessly for both NumPy and CuPy
print(B)
Good luck!