pythonnumpycupy

Cupy creating array using other variables


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?


Solution

  • 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!