pyepri.backends

This module defines a standardized Backend class for unified use of numpy, cupy and torch libraries (see <class ‘backends.Backend’>).

This module also provides constructors of ready-to-use backends, see

A backends.Backend instance provides attributes for data type mapping (the standardized type is a str, e.g., 'float32', 'float64', 'int32', …, is mapped to the library dependent corresponding data types).

A Backend instance also provides lambda function attributes that are mapped to several standard lib-dependent functions (lib.zeros, lib.arange, lib.sin, lib.cos, lib.meshgrid, …, for lib in {numpy, cupy, torch}) but with unified fashion (parameters naming, default settings, etc).

For more details about how backend system works, you may consult the Backend class documentation by typing:

>>> import pyepri.backends as backends
>>> help(backends.Backend)

Examples of backend instantiation and utilization are also described in the constructor documentations:

>>> import pyepri.backends as backends
>>> help(backends.create_numpy_backend) # numpy backend constructor
>>> help(backends.create_cupy_backend)  # cupy backend constructor
>>> help(backends.create_torch_backend) # torch backend constructor

Classes

Backend

Class for mapping our standardized data types and methods to the specified library (numpy, torch, cupy).

Functions

create_numpy_backend()

Create a numpy backend.

create_cupy_backend()

Create a cupy backend.

create_torch_backend(device)

Create a torch backend with specified device.

Module Contents

pyepri.backends.create_numpy_backend()[source]

Create a numpy backend.

Returns:

backend – Backend configured to perform operations using numpy library on CPU device.

Return type:

<class ‘backends.Backend’>

See also

backends.create_cupy_backend, backends.create_torch_backend

Examples

The following example illustrate how one can call the numpy.zeros and numpy.arange functions from a backend output of this create_numpy_backend() function.

>>> import pyepri.backends as backends
>>> b = backends.create_numpy_backend()
>>> x = b.zeros((10,),dtype='float64')
>>> print(x)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
>>> print(type(x))
<class 'numpy.ndarray'>
>>> print(x.dtype)
float64
>>> y = b.arange(3,10,2,dtype='int32') # another example
>>> print(y)
[3 5 7 9]
>>> z = b.arange(10) # b.arange behaves as its numpy counterpart
>>>                  # (`start` and `end` parameters are optional
>>>                  # as for numpy.arange)
>>> print(z)
[0 1 2 3 4 5 6 7 8 9]
pyepri.backends.create_cupy_backend()[source]

Create a cupy backend.

Returns:

backend – Backend configured to perform operations using cupy library on GPU device.

Return type:

<class ‘backends.Backend’>

See also

backends.create_numpy_backend, backends.create_torch_backend

Examples

The following example illustrate how one can call the cupy.zeros and cupy.arange functions from a backend output of this backends.create_cupy_backend function.

>>> import pyepri.backends as backends
>>> b = backends.create_cupy_backend()
>>> x = b.zeros((10,),dtype='float64')
>>> print(x)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
>>> print(type(x))
<class 'cupy.ndarray'>
>>> print(x.dtype)
float64
>>> print(x.device) # should return a GPU device (e.g.,
>>>                 # <CUDA Device 0>)
<CUDA Device 0>
>>> y = b.arange(3,10,2,dtype='int32') # another example
>>> print(y)
[3 5 7 9]
>>> z = b.arange(10) # b.arange behaves as its cuppy counterpart
>>>                  # (`start` and `end` parameters are optional
>>>                  # as for cupy.arange)
>>> print(z)
[0 1 2 3 4 5 6 7 8 9]
pyepri.backends.create_torch_backend(device)[source]

Create a torch backend with specified device.

Parameter

devicestr

One of {‘cpu’, ‘cuda’, ‘cuda:X’} with X = device index.

returns:

backend – A backends.Backend instance configured to perform operations using torch library on the specified device.

rtype:

<class ‘backends.Backend’>

Notes

This function will raise an error if torch library is not installed or if the input device argument is not available on your system.

See also

backends.create_numpy_backend, backends.create_cupy_backend

Examples

The following example illustrate how one can call the torch.zeros and torch.arange functions from a backend output of this create_torch_backend() function.

Example 1: create and use a torch backend on ‘cpu’ device

To run the following example torch library should be installed on your system (otherwise the second line of this example will raise an error).

>>> import pyepri.backends as backends
>>> b = backends.create_torch_backend('cpu')
>>> x = b.zeros((10,),dtype='float64') # type is specified using a
>>>                                    # str (here 'float64')
>>> print(x)
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       dtype=torch.float64)
>>> print(x.device) # device is 'cpu' because 'cpu' was used as
>>>                 # input in line 2 of this example
cpu
>>> y = b.arange(3,10,2,dtype='int32') # another example
>>> print(y)
tensor([3, 5, 7, 9], dtype=torch.int32)
>>> print(y.device)
cpu
>>> z = b.arange(10) # b.arange behaves as its torch counterpart
>>>                  # (`start` and `end` parameters are optional
>>>                  # as for torch.arange)
>>> print(z)
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

Example 2: create and use a torch backend on ‘cuda’ device

To run the following example torch library should be installed on your system and ‘cuda’ device should be available (otherwise the second line of this example will raise an error)

>>> import pyepri.backends as backends
>>> b = backends.create_torch_backend('cuda')
>>> x = b.zeros((10,),dtype='float64') # type is specified using a
>>>                                    # str (here 'float64')
>>> print(x) # device is now cuda:0 (can be cuda:X with X = one
>>>          # available device index on your own system)
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], device='cuda:0',
       dtype=torch.float64)
>>> y = b.arange(3,10,2,dtype='int32') # another example
>>> print(y)
tensor([3, 5, 7, 9], device='cuda:0', dtype=torch.int32)
>>> z = b.arange(10)
>>> print(z)
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], device='cuda:0')
class pyepri.backends.Backend(lib, device)[source]

Class for mapping our standardized data types and methods to the specified library (numpy, torch, cupy).

Type correspondences

This package relies on data types specified with a str from which the library dependent data types will be inferred. The correspondence between the different data types is provided in the following Table.

str

lib = numpy or cupy

lib = torch

'int8'

lib.dtype('int8')

torch.int8

'int16'

lib.dtype('int16')

torch.int16

'int32'

lib.dtype('int32')

torch.int32

'int64'

lib.dtype('int64')

torch.int64

'float32'

lib.dtype('float32')

torch.float32

'float64'

lib.dtype('float64')

torch.float64

'complex64'

lib.dtype('complex64')

torch.complex64

'complex128'

lib.dtype('complex128')

torch.complex128

None

lib.dtype(None)

None

The mapping between the data types in str format and those of the targeted library can be done using the dtypes and invdtypes dictionary attributes described below.

We also provide a str to str mapping towards complex data types (non invertible mapping) :

data type

complex data type

'int8'

None

'int16'

None

'int32'

'complex64'

'float32'

'complex64'

'complex64'

'complex64'

'int64'

'complex128'

'float64'

'complex128'

'complex128'

'complex128'

The above mapping can be used to infer the data type of a complex array computed from another (non necessarily complex) input array (e.g., infer the data type of the discrete Fourier transform of an input array).

Contextual attributes

lib<class ‘module’>

One of {numpy, cupy, torch}.

devicestr

Device identifier (see constructor documentation below).

cls<class ‘type’>

Native array data type in the backend library, as described below.

lib

cls

numpy

<class ‘numpy.ndarray’>

cupy

<class ‘cupy.ndarray’>

torch

<class ‘torch.Tensor’>

Data type mapping attributes

str_to_lib_dtypesdict

Mapping data types in standardized str format -> lib dependent data types (see above).

lib_to_str_dtypesdict

Invert of the str_to_lib_dtypes mapping (lib dependent data types -> data types in standardized str format).

mapping_to_complex_dtypesdict

Mapping from data type in standardized str format -> complex data type in standardized str format (see above).

Other attributes (library-dependent methods)

An instance backend with class pyepri.backends.Backend remaps library dependent methods to basically the same methods coming from backend.lib but with standardized usage (e.g. backend.meshgrid can remap to {numpy or cupy or torch}.meshgrid depending on backend.lib).

A mini documentation is provided for each standardized method and can be displayed using the help() function, as illustrated below.

>>> import pyepri.backends as backends
>>> backend = backends.create_numpy_backend()
>>> help(backend.meshgrid)
...Help on function <lambda> in module pyepri.backends:
...
...<lambda> lambda *xi, indexing='xy'
...    return numpy.meshgrid(*xi, indexing=indexing)
lib
is_backend_compliant
mapping_to_complex_dtypes
lib_to_str_dtypes
sqrt
sin
cos
arccos
arctan2
real
argwhere
abs
tile
moveaxis
meshgrid
exp
__doc__