Run Python NumPy code on distributed heterogeneous systems without changing a single line of code.

Run One Program at any Scale with Legate

Python is one of the most used languages today, particularly in machine learning. Python itself doesn’t have a numerically focused data type, so NumPy was created to fill that void, adding support for large, multidimensional arrays and matrices, as well as contributing a good collection of math functions for these arrays and matrices.

NumPy

Most people who code in Python have seen or written NumPy code, but just in case you have not, this quick example creates an “empty” 2D array of size nx by ny:

import numpy as np

nx = 10
ny = 10

a = np.empty((nx,ny))
type(a)

Array a is of data type numpy.ndarray, with which you can do all sorts of things, including adding, subtracting, multiplying, and performing other mathematical operations. Another example is to solve the linear equation, Ax = b:

import numpy as np

nx = 100
ny = 100

a = np.random.rand(nx,ny)
b = np.random.rand(ny)

x = np.linalg.solve(a, b)

Array a and the second part of the tuple, b, are created by a random number generator with random samples from a uniform distribution over [0,1). The equation is then solved by the solve routine.

NumPy on GPUs

NumPy functions are all single threaded unless the underlying NumPy code is multithreaded. GPUs are monsters for matrix computations, but NumPy itself does not run on GPUs. CuPy was created as an almost complete NumPy-compatible library that runs on GPUs. Most routines use a single GPU for running code, although porting functions to use multiple GPUs requires work. CuPy is even adding functions from SciPy to its codebase. As with NumPy, CuPy is open source.

CuPy code looks virtually identical to NumPy code:

import cupy as cp

nx = 10
ny = 10

a = cp.empty(nx,ny)
type(a)

Notice that the CuPy data type is different from NumPy because it is specific to the GPU. However, commands can move data back and forth to the GPU, converting data types for you.

As mentioned previously, you can run almost any NumPy code with CuPy. The second NumPy example is written as CuPy:

import cupy as cp

nx = 100
ny = 100

a = cp.random.rand(nx,ny)
b = cp.random.rand(ny)

x = cp.linalg.solve(a, b)

To run NumPy code on GPUs with CuPy, you need to change your code. Generally, it is not difficult to port most NumPy code to CuPy. Mixing NumPy and CuPy requires extra coding to move data back and forth while paying attention to data types and data location, which increases the amount of code and adds complexity. Moreover, without careful coding, the code would no longer be portable because it needs a CPU and a GPU.