########################################
# MIT License
#
# Copyright (c) 2020 Miguel Ramos Pernas
########################################
'''
Application interface to deal with arrays. The different array classes
can not define the __getitem__ since numpy objects would use it to do
arithmetic operations.
'''
from . import core
from ..base import data_types
import contextlib
__all__ = ['ArrayOperations']
[docs]class ArrayOperations(object):
def __init__(self, backend, **kwargs):
'''
Build the object to do operations within a backend. Only the necessary
operators have been defined.
:param backend: backend where the operations will be done.
:type backend: Backend
:param kwargs: arguments forwarded to the backend constructor
(cuda and opencl backends only).
:type kwargs: dict
The possible keyword arguments in GPU backends are:
* *device*: device to be used.
* *interactive*: whether to ask the user a device if not specified or if the proposed device is not available.
'''
super().__init__()
self.__backend = backend
if self.__backend.btype == core.CPU:
from .cpu import CPUOperations
self.__oper = CPUOperations(backend, **kwargs)
else:
from .gpu import GPUOperations
self.__oper = GPUOperations(backend, **kwargs)
@property
def backend(self):
'''
Backend interface.
'''
return self.__backend
[docs] def access_pdf(self, name, ndata_pars, nvar_arg_pars=None):
'''
Access the PDF with the given name.
:param name: name of the PDF.
:type name: str
:param ndata_pars: number of data parameters.
:type ndata_pars: int
:param nvar_arg_pars: number of variable arguments.
:type nvar_arg_pars: int or None
:returns: proxy for the different evaluation functions.
:rtype: FunctionsProxy
:raises FileNotFoundError: If the XML file associated to the PDF can not be found.
'''
return self.__oper.access_pdf(name, ndata_pars, nvar_arg_pars)
[docs] def carange(self, n):
'''
Create an array of elements from 0 to n.
:param n: the next to last item in the array.
:type n: int
:returns: output array.
:rtype: carray
'''
return self.__oper.carange(n)
[docs] def iarange(self, n):
'''
Create an array of elements from 0 to n.
:param n: the next to last item in the array.
:type n: int
:returns: output array.
:rtype: iarray
'''
return self.__oper.iarange(n)
[docs] def ndarray_to_backend(self, a):
'''
Create an array in the backend of this object. This will directly
create an object of the underlying array type of the :class:`minkit.barray`,
:class:`minkit.farray` or :class:`minkit.iarray` instance.
:param a: input array.
:type a: numpy.ndarray
:returns: array representation in the given backend.
:rtype: numpy.ndarray or reikna.cluda.api.Array
.. warning:: This function must be used carefully since the output array
does not track to which backend it belongs.
'''
return self.__oper.ndarray_to_backend(a)
[docs] def concatenate(self, arrays, maximum=None):
'''
Concatenate several arrays. If "maximum" is specified, the last elements
will be dropped, so the length of the output array is "maximum".
:param arrays: collection of arrays.
:type arrays: tuple(farray)
:param maximum: possible maximum length of the output array.
:type maximum: int or None
:returns: concatenated array.
:rtype: darray
'''
return self.__oper.concatenate(tuple(a for a in arrays), maximum)
[docs] def concatenated_linspace(self, edges, nsteps):
'''
Create a set of concatenated arrays obtained from linspace calls with
the given edges.
:param edges: edges of the bins.
:type edges: darray
:param nsteps: number of steps in the arrays.
:type nsteps: int
:returns: Concatenated array.
:rtype: darray
'''
return self.__oper.concatenated_linspace(edges, nsteps)
[docs] def count_nonzero(self, a):
'''
Count the number of elements that are different from zero.
:param a: input array.
:type a: barray
:returns: number of elements that are different from zero.
:rtype: int
'''
return self.__oper.count_nonzero(a)
[docs] def bempty(self, size):
'''
Create an empty :class:`barray` instance with the given length.
:param size: length of the output array.
:type size: int
:returns: empty array.
:rtype: barray
'''
return self.__oper.bempty(size)
[docs] def dempty(self, size, ndim=1):
'''
Create an empty :class:`darray` instance with the given length.
:param size: length of the output array.
:type size: int
:param ndim: number of dimensions of the output array.
:type ndim: int
:returns: empty array.
:rtype: darray
'''
return self.__oper.dempty(size, ndim)
[docs] def iempty(self, size):
'''
Create an empty :class:`iarray` instance with the given length.
:param size: length of the output array.
:type size: int
:returns: empty array.
:rtype: iarray
'''
return self.__oper.iempty(size)
[docs] def cexp(self, a):
'''
Calculate the exponential of an array of numbers.
:param a: input array.
:type a: carray
:returns: exponential values.
:rtype: carray
'''
return self.__oper.cexp(a)
[docs] def dexp(self, a):
'''
Calculate the exponential of an array of numbers. The input array must
be unidimensional.
:param a: input array.
:type a: darray
:returns: exponential values.
:rtype: darray
'''
return self.__oper.dexp(a)
[docs] def fftconvolve(self, a, b, data):
'''
Calculate the convolution of two arrays that correspond to the output
from the evaluation of two PDFs in the given data.
:param a: first array.
:type a: farray
:param b: second array.
:type b: farray
:param data: array of data (only 1D is supported).
:type data: farray
:returns: convolution of the two arrays.
:rtype: darray
'''
return self.__oper.fftconvolve(a, b, data)
[docs] def ge(self, a, b):
r'''
Compare two arrays :math:`a \geq b`. The input arrays must be unidimensional.
:param a: first array.
:type a: farray
:param b: second array.
:type b: farray
:returns: result of the comparison.
:rtype: barray
'''
return self.__oper.ge(a, b)
[docs] def make_linear_interpolator(self, xp, yp):
'''
Create a function that, given an array of points in *x*, provides the
interpolated values using a linear interpolator.
:param xp: reference points in the x axis.
:type xp: darray
:param yp: reference points in the y axis.
:type yp: darray
:returns: interpolator.
'''
return self.__oper.make_linear_interpolator(xp, yp)
[docs] def make_spline_interpolator(self, xp, yp):
'''
Create a function that, given an array of points in *x*, provides the
interpolated values using an spline of order 3.
:param xp: reference points in the x axis.
:type xp: darray
:param yp: reference points in the y axis.
:type yp: darray
:returns: interpolator.
'''
return self.__oper.make_spline_interpolator(xp, yp)
[docs] def product_by_zero_is_zero(self, f, s):
'''
Do a product of two arrays in such a way that if any of the elements
is zero the output will be zero.
:param f: first array.
:type f: darray
:param s: second array.
:type f: darray
:returns: Product of the two arrays.
:rtype: darray
'''
return self.__oper.product_by_zero_is_zero(f, s)
@property
def bool_type(self):
'''
Data type used for boolean comparisons.
'''
if core.is_gpu_backend(self.__backend.btype):
return data_types.cpu_bool
else:
return data_types.cpu_real_bool
[docs] def argmax(self, a):
'''
Return the index with the maximum value.
:param a: input array.
:type a: marray, numpy.ndarray or reikna.cluda.api.Array
:returns: Index with the maximum value.
:rtype: int
'''
return self.__oper.argmax(a)
[docs] def is_bool(self, a):
'''
Check whether the data type of the given array is of boolean type.
:param a: input array.
:type a: marray, numpy.ndarray or reikna.cluda.api.Array
:returns: decision.
:rtype: bool
'''
return a.dtype == self.bool_type
[docs] def is_complex(self, a):
'''
Check whether the data type of the given array is of complex type.
:param a: input array.
:type a: marray, numpy.ndarray or reikna.cluda.api.Array
:returns: decision.
:rtype: bool
'''
return a.dtype == data_types.cpu_complex
[docs] def is_float(self, a):
'''
Check whether the data type of the given array is of floating point type.
:param a: input array.
:type a: marray, numpy.ndarray or reikna.cluda.api.Array
:returns: decision.
:rtype: bool
'''
return a.dtype == data_types.cpu_float
[docs] def is_int(self, a):
'''
Check whether the data type of the given array is of integral type.
:param a: input array.
:type a: marray, numpy.ndarray or reikna.cluda.api.Array
:returns: decision.
:rtype: bool
'''
return a.dtype == data_types.cpu_int
[docs] def is_inside(self, data, lb, ub):
'''
Return the decision whether the input data is within the given bounds
or not.
:param data: input data array.
:type data: farray
:param lb: lower bounds.
:type lb: numpy.ndarray
:param ub: upper bounds.
:type ub: numpy.ndarray
:returns: array with the decisions.
:rtype: barray
'''
return self.__oper.is_inside(data, lb, ub)
[docs] def le(self, a, b):
r'''
Compare two arrays :math:`a \leq b`. The input arrays must be unidimensional.
:param a: first array.
:type a: farray
:param b: second array.
:type b: farray
:returns: result of the comparison.
:rtype: barray
'''
return self.__oper.le(a, b)
[docs] def lt(self, a, b):
r'''
Compare two arrays :math:`a < b`. The input arrays must be unidimensional.
:param a: first array.
:type a: farray
:param b: second array.
:type b: farray
:returns: result of the comparison.
:rtype: barray
'''
return self.__oper.lt(a, b)
[docs] def linspace(self, vmin, vmax, size):
'''
Create an array where the values are in increasing order by a constant
step. Points in "vmin" and "vmax" are included.
:param vmin: where to start generating values.
:type vmin: float
:param vmax: where to end generating values.
:type vmax: float
:param size: length of the output array.
:type size: int
:returns: array.
:rtype: darray
'''
return self.__oper.linspace(vmin, vmax, size)
[docs] def log(self, a):
'''
Calculate the logarithm of an array of numbers. The input array must
be unidimensional.
:param a: input array.
:type a: farray
:returns: logarithm values.
:rtype: darray
'''
return self.__oper.log(a)
[docs] def logical_and(self, a, b, out=None):
'''
Do the logical "and" operation element by element.
:param a: first array.
:type a: farray
:param b: second array.
:type b: farray
:param out: possible output array to write the data.
:type out: barray or None
:returns: array of decisions.
:rtype: barray
'''
return self.__oper.logical_and(a, b, out)
[docs] def logical_or(self, a, b, out=None):
'''
Do the logical "or" operation element by element.
:param a: first array.
:type a: farray
:param b: second array.
:type b: farray
:param out: possible output array to write the data.
:type out: barray or None
:returns: array of decisions.
:rtype: barray
'''
return self.__oper.logical_or(a, b, out)
[docs] def max(self, a):
'''
Calculate the maximum value in an array.
:param a: input array.
:type a: farray
:returns: maximum value.
:rtype: float
'''
return self.__oper.max(a)
[docs] def meshgrid(self, lb, ub, size):
r'''
Create a grid of values using the given set of bounds. The size can
be specified as a single value or as a set of sizes for each dimension.
If it is a single value, then the output array will be of length
:math:`size \times size \times ...`, depending on the number of dimensions.
:param lb: lower bounds.
:type lb: numpy.ndarray
:param ub: upper bounds.
:type ub: numpy.ndarray
:param size: size of the grid to generate.
:type size: numpy.ndarray
'''
return self.__oper.meshgrid(lb, ub, size)
[docs] def min(self, a):
'''
Calculate the minimum value in an array.
:param a: input array.
:type a: farray
:returns: minimum value.
:rtype: float
'''
return self.__oper.min(a)
[docs] def bones(self, size):
'''
Create an array of "true" values of the given size.
:param size: length of the output array.
:type size: int
:returns: array of "true".
:rtype: barray
'''
return self.__oper.bones(size)
[docs] def bzeros(self, size):
'''
Create an array of "false" values of the given size.
:param size: length of the output array.
:type size: int
:returns: array of "false".
:rtype: barray
'''
return self.__oper.bzeros(size)
[docs] def dones(self, size):
'''
Create an array of the given size filled with ones.
:param size: length of the output array.
:type size: int
:returns: array of ones.
:rtype: darray
'''
return self.__oper.dones(size)
[docs] def dzeros(self, size, ndim=1):
'''
Create an array of the given size filled with zeros.
:param size: length of the output array.
:type size: int
:param ndim: number of dimensions of the output array.
:type ndim: int
:returns: array of zeros.
:rtype: darray
'''
return self.__oper.dzeros(size, ndim)
[docs] def random_grid(self, lb, ub, size):
r'''
Create a random grid using the given bounds. The size can be specified
as a single value or as a set of sizes for each dimension. If it is a
single value, then the output array will be of length
:math:`size \times size \times ...`, depending on the number of dimensions.
:param lb: lower bounds.
:type lb: numpy.ndarray
:param ub: upper bounds.
:type ub: numpy.ndarray
:param size: size of the grid.
:type size: int or numpy.ndarray
:returns: random grid.
:rtype: darray
'''
return self.__oper.random_grid(lb, ub, size)
[docs] def restrict_data_size(self, maximum, data):
'''
Drop the last elements of an array of data.
:param maximum: maximum length of the data sample.
:type maximum: int
:param data: data sample.
:type data: farray
:returns: reduced data array.
:rtype: darray
'''
return self.__oper.restrict_data_size(maximum, data)
[docs] def set_rndm_seed(self, seed):
'''
Set the seed of the random number generator.
:param seed: new seed to use.
:type seed: int
'''
self.__oper.set_rndm_seed(seed)
[docs] def shuffling_index(self, n):
'''
Calculate a set of indices to shuffle an array.
:param n: length of the array.
:type n: int
:returns: array of indices.
:rtype: iarray
'''
return self.__oper.shuffling_index(n)
[docs] def simpson_factors(self, size, nbins=None):
'''
Return an array with the coefficients for the Simpson numerical
integration method.
:param size: number of steps.
:type size: int
:param nbins: if provided, the factors are returned assuming that
there are *size* values for each bin.
:param nbins: int or None
:returns: Array with the coefficients.
:rtype: darray
'''
if size % 2 == 0:
raise ValueError('Size must be an odd number')
return self.__oper.simpson_factors(size, nbins)
[docs] def steps_from_edges(self, edges):
'''
Calculate the bin sizes from the given set of edges. Only works for
one-dimensional arrays.
:param edges: edges defining the bins.
:type edges: darray
:returns: step for each bin.
:rtype: darray
'''
return self.__oper.steps_from_edges(edges)
[docs] def sum(self, a):
'''
Sum the elements of the given array.
:param a: input array.
:type a: farray
:returns: sum of elements.
:rtype: float
'''
return self.__oper.sum(a)
[docs] def sum_arrays(self, arrays):
'''
Sum arrays element by element.
:param arrays: set of arrays.
:type arrays: tuple(farray)
:returns: array with the sum.
:rtype: darray
'''
out = self.dzeros(len(arrays[0]))
for a in arrays:
out += a
return out
[docs] def sum_inside(self, indices, gaps, centers, edges, values=None):
'''
Sum the occurrences inside the given bounds. If "values" is specified,
then the values in it are used instead.
:param indices: array of indices.
:type indices: numpy.ndarray
:param gaps: gaps to access the data.
:type gaps: numpy.ndarray
:param centers: centers corresponding to a data sample.
:type centers: farray
:param edges: edges defining the bins.
:type edges: farray
:param values: possible values (or weights) associated to the data sample.
:type values: farray or None
:returns: sum inside each bin.
:rtype: darray
'''
return self.__oper.sum_inside(indices, gaps, centers, edges, values)
[docs] def slice_from_boolean(self, a, v):
'''
Get a slice of the given array using a mask array.
:param a: input data array.
:type a: farray
:param v: mask array.
:type v: barray
:returns: slice of the array.
:rtype: darray
'''
return self.__oper.slice_from_boolean(a, v)
[docs] def slice_from_integer(self, a, i):
'''
Get a slice of the given array using an array of indices.
:param a: input data array.
:type a: farray
:param i: array of indices.
:type i: iarray
:returns: slice of the array.
:rtype: darray
'''
return self.__oper.slice_from_integer(a, i)
[docs] def take_column(self, a, i=0):
'''
Take elements of the array using a period.
:param a: input array.
:type a: farray
:param i: column to take the elements.
:type i: int
:returns: reduced array.
:rtype: darray
'''
return self.__oper.take_column(a, i)
[docs] def take_slice(self, a, start=0, end=None):
'''
Take a slice of entries from the array.
:param a: input array.
:type a: farray
:param start: where to start taking entries.
:type start: int
:param end: where to end taking entries.
:type end: int or None
:returns: slice of the array.
:rtype: darray
'''
end = end if end is not None else len(a)
return self.__oper.take_slice(a, start, end)
[docs] @contextlib.contextmanager
def using_caches(self):
'''
Use the caches (GPU only). Only those caches related to arrays and functions
(not the PDFs) will be removed.
'''
if core.is_gpu_backend(self.__backend.btype):
with self.__oper.using_caches():
yield self
else:
yield self