diff --git a/hysop/backend/device/codegen/symbolic/expr.py b/hysop/backend/device/codegen/symbolic/expr.py index 1ee0e5a7e0aaaa82469a21ba4a009d3d59332056..acea3e347f203cc6a90406c6f5de53014ac68bf9 100644 --- a/hysop/backend/device/codegen/symbolic/expr.py +++ b/hysop/backend/device/codegen/symbolic/expr.py @@ -3,15 +3,16 @@ from hysop.symbolic import Symbol, Expr from hysop.symbolic.array import OpenClSymbolicBuffer, OpenClSymbolicNdBuffer from hysop.tools.types import check_instance, first_not_None, to_tuple, to_list from hysop.tools.numerics import is_fp, is_signed, is_unsigned, is_integer, is_complex -from sympy.printing.ccode import C99CodePrinter +from sympy.printing.c import C99CodePrinter from hysop.backend.device.codegen.base.variables import ctype_to_dtype from hysop.backend.device.opencl.opencl_types import basetype as cl_basetype, \ - components as cl_components, \ - vtype as cl_vtype + components as cl_components, \ + vtype as cl_vtype InstructionTermination = '' + class TypedI(object): def __new__(cls, *args, **kwds): positive = kwds.pop('positive', None) @@ -63,12 +64,14 @@ class TypedI(object): def is_positive(self): return first_not_None(self._positive, self.is_unsigned) + class TypedSymbol(TypedI, Symbol): def __new__(cls, ctype, **kwds): obj = super(TypedSymbol, cls).__new__(cls, **kwds) obj.ctype = ctype return obj + class TypedExpr(TypedI, Expr): def __new__(cls, ctype, *args): try: @@ -79,13 +82,16 @@ class TypedExpr(TypedI, Expr): obj.ctype = ctype return obj + class TypedExprWrapper(TypedExpr): def __new__(cls, ctype, expr): - obj = super(TypedExprWrapper, cls).__new__(cls, ctype, expr) - obj.expr = expr - return obj + obj = super(TypedExprWrapper, cls).__new__(cls, ctype, expr) + obj.expr = expr + return obj + def _ccode(self, printer): - return printer._print(self.expr) + return printer._print(self.expr) + class OpenClConvert(TypedExpr): def __new__(cls, ctype, expr): @@ -98,6 +104,7 @@ class OpenClConvert(TypedExpr): cast = 'convert_{}({})'.format(self.ctype, val) return cast + class OpenClCast(TypedExpr): def __new__(cls, ctype, expr): obj = super(OpenClCast, cls).__new__(cls, ctype, expr) @@ -109,6 +116,7 @@ class OpenClCast(TypedExpr): cast = '({})({})'.format(self.ctype, expr) return cast + class OpenClBool(TypedExpr): """ Convert a scalar boolean condition (ie. a int in OpenCL) @@ -117,10 +125,11 @@ class OpenClBool(TypedExpr): """ def __new__(cls, expr): assert expr.ctype in ('short', 'int', 'long'), ctype - ctype='char' # force lowest integer rank (to force promotion later) + ctype = 'char' # force lowest integer rank (to force promotion later) obj = super(OpenClBool, cls).__new__(cls, ctype, expr) obj.expr = expr return obj + def _ccode(self, printer): # negate scalar boolean to set all bits to 1 (unsigned -1 sets all bits) # (unsigned 0 has not bit set) @@ -136,21 +145,24 @@ class OpenClBool(TypedExpr): return s + class Return(Expr): def __new__(cls, expr): obj = super(Return, cls).__new__(cls, expr) obj.expr = expr return obj + def _ccode(self, printer): expr = printer._print(self.expr) code = 'return {};'.format(expr) ret = printer.codegen.append(code) return InstructionTermination + class NumericalConstant(TypedExpr): def __new__(cls, ctype, value): obj = super(NumericalConstant, cls).__new__(cls, ctype, value) - obj.value=value + obj.value = value return obj def _ccode(self, printer): @@ -161,15 +173,21 @@ class NumericalConstant(TypedExpr): ctype = typegen.dumped_type(val) return cls(ctype, val) + class IntegerConstant(NumericalConstant): pass + + class FloatingPointConstant(NumericalConstant): pass + + class ComplexFloatingPointConstant(NumericalConstant): def _ccode(self, printer): return '(({})({}, {}))'.format(self.ctype, - printer.typegen.dump(self.value.real), - printer.typegen.dump(self.value.imag)) + printer.typegen.dump(self.value.real), + printer.typegen.dump(self.value.imag)) + class OpenClVariable(TypedExpr): def __new__(cls, ctype, var, *args): @@ -180,6 +198,7 @@ class OpenClVariable(TypedExpr): def _ccode(self, printer): return self.var() + class OpenClIndexedVariable(OpenClVariable): def __new__(cls, ctype, var, index): try: @@ -201,13 +220,14 @@ class OpenClIndexedVariable(OpenClVariable): pass var = printer._print(self.var) - if (self.dim>1): + if (self.dim > 1): vals = ', '.join('{}[{}]'.format(var, self.index.var[i]) for i in range(self.dim)) return '({})({})'.format(self.ctype, vals) else: index = printer._print(self.index) return '{}[{}]'.format(var, index) + class OpenClAssignment(TypedExpr): def __new__(cls, ctype, var, op, rhs): obj = super(OpenClAssignment, cls).__new__(cls, ctype, var, op, rhs) @@ -215,6 +235,7 @@ class OpenClAssignment(TypedExpr): obj.op = op obj.rhs = rhs return obj + def _ccode(self, printer): var = printer._print(self.var) rhs = printer._print(self.rhs) @@ -222,6 +243,7 @@ class OpenClAssignment(TypedExpr): printer.codegen.append(code) return InstructionTermination + class FunctionCall(TypedExpr): def __new__(cls, ctype, fn, fn_kwds): obj = super(FunctionCall, cls).__new__(cls, ctype, fn, fn_kwds) @@ -235,6 +257,7 @@ class FunctionCall(TypedExpr): def _sympystr(self, printer): return 'FunctionCall({})'.format(self.fn.name) + class VStore(Expr): def __new__(cls, ptr, offset, data, n=1, **opts): obj = super(VStore, cls).__new__(cls, ptr, offset, data, n) @@ -247,10 +270,11 @@ class VStore(Expr): def _ccode(self, printer): code = printer.codegen.vstore(n=self.n, ptr=self.ptr, - offset=self.offset, data=self.data, **self.opts) + offset=self.offset, data=self.data, **self.opts) printer.codegen.append(code) return InstructionTermination + class VStoreIf(VStore): def __new__(cls, cond, scalar_cond, ptr, offset, data, n, **opts): obj = super(VStoreIf, cls).__new__(cls, ptr, offset, data, n) @@ -261,9 +285,9 @@ class VStoreIf(VStore): def _ccode(self, printer): printer.codegen.vstore_if(cond=self.cond, - scalar_cond=self.scalar_cond, - n=self.n, ptr=self.ptr, - offset=self.offset, data=self.data, **self.opts) + scalar_cond=self.scalar_cond, + n=self.n, ptr=self.ptr, + offset=self.offset, data=self.data, **self.opts) return InstructionTermination @@ -279,13 +303,14 @@ class VLoad(TypedExpr): def _ccode(self, printer): vload = printer.codegen.vload(n=self.n, ptr=self.ptr, - offset=self.offset, **self.opts) + offset=self.offset, **self.opts) if self.dst: self.dst.affect(printer.codegen, vload) return InstructionTermination else: return vload + class VLoadIf(VLoad): def __new__(cls, cond, scalar_cond, ptr, offset, dst, n, default_value, **opts): obj = super(VLoadIf, cls).__new__(cls, ptr, offset, dst, n) @@ -297,20 +322,21 @@ class VLoadIf(VLoad): def _ccode(self, printer): printer.codegen.vload_if(cond=self.cond, - scalar_cond=self.scalar_cond, - n=self.n, ptr=self.ptr, - offset=self.offset, dst=self.dst, - default_value=self.default_value, **self.opts) + scalar_cond=self.scalar_cond, + n=self.n, ptr=self.ptr, + offset=self.offset, dst=self.dst, + default_value=self.default_value, **self.opts) return InstructionTermination + class IfElse(Expr): def __new__(cls, conditions, all_exprs, else_exprs=None): - conditions = to_tuple(conditions) - all_exprs = to_list(all_exprs) - else_exprs = to_list(else_exprs) if (else_exprs is not None) else None - assert len(all_exprs)>=1 + conditions = to_tuple(conditions) + all_exprs = to_list(all_exprs) + else_exprs = to_list(else_exprs) if (else_exprs is not None) else None + assert len(all_exprs) >= 1 if not isinstance(all_exprs[0], list): - assert len(conditions)==1 + assert len(conditions) == 1 all_exprs = [all_exprs] assert len(conditions) == len(all_exprs) >= 1 obj = super(IfElse, cls).__new__(cls, conditions, all_exprs, else_exprs) @@ -331,6 +357,7 @@ class IfElse(Expr): printer._print(e) return InstructionTermination + class UpdateVars(Expr): def __new__(cls, srcs, dsts, ghosts): obj = super(UpdateVars, cls).__new__(cls, srcs, dsts, ghosts) @@ -341,18 +368,18 @@ class UpdateVars(Expr): return obj def init(self, srcs, dsts, ghosts): - assert len(srcs)==len(dsts) - private_stores = () + assert len(srcs) == len(dsts) + private_stores = () local_stores = () for (src, dst, ghost) in zip(srcs, dsts, ghosts): assert not src.is_ptr if dst.is_ptr: - assert dst.storage=='__local' + assert dst.storage == '__local' local_stores += ((src, dst, ghost),) else: private_stores += ((src, dst),) self.private_stores = private_stores - self.local_stores = local_stores + self.local_stores = local_stores def _ccode(self, printer): codegen = printer.codegen @@ -368,16 +395,16 @@ class UpdateVars(Expr): for (src, dst) in self.private_stores: dst.affect(al, init=src, align=True) if self.local_stores: - srcs = tuple(map(lambda x: x[0], self.local_stores)) - ptrs = tuple(map(lambda x: x[1], self.local_stores)) + srcs = tuple(map(lambda x: x[0], self.local_stores)) + ptrs = tuple(map(lambda x: x[1], self.local_stores)) offsets = tuple(map(lambda x: x[2], self.local_stores)) codegen.multi_vstore_if(csc.is_last_active, - lambda i: '{}+{} < {}'.format(csc.full_offset, i, csc.compute_grid_size[0]), - csc.vectorization, csc.local_offset, - srcs, ptrs, - extra_offsets=offsets, - use_short_circuit=csc.use_short_circuit, - else_cond=csc.is_active) + lambda i: '{}+{} < {}'.format(csc.full_offset, i, csc.compute_grid_size[0]), + csc.vectorization, csc.local_offset, + srcs, ptrs, + extra_offsets=offsets, + use_short_circuit=csc.use_short_circuit, + else_cond=csc.is_active) codegen.barrier(_local=True) return InstructionTermination @@ -393,6 +420,7 @@ class BuiltinFunctionCall(TypedExpr): def _ccode(self, printer): return '{}({})'.format(self.fname, ', '.join(printer._print(arg) for arg in self.fargs)) + class BuiltinFunction(object): def __new__(cls, fname): obj = super(BuiltinFunction, cls).__new__(cls) @@ -424,10 +452,9 @@ class OpenClPrinter(C99CodePrinter): def doprint(self, expr, terminate=True): res = super(OpenClPrinter, self).doprint(expr) if terminate and (res != InstructionTermination): - msg='OpenClPrinter failed to generate code for the following expression:\n' - msg+=' {}\n'.format(expr) - msg+='Returned value was:\n {}\n'.format(res) + msg = 'OpenClPrinter failed to generate code for the following expression:\n' + msg += ' {}\n'.format(expr) + msg += 'Returned value was:\n {}\n'.format(res) raise RuntimeError(msg) if not terminate: return res - diff --git a/hysop/backend/device/opencl/opencl_array.py b/hysop/backend/device/opencl/opencl_array.py index c6030546c176b45f2bb34cdeafef0af7c0421e4b..7e86a24583f39189afaadc3b43db3f09dc7a97d4 100644 --- a/hysop/backend/device/opencl/opencl_array.py +++ b/hysop/backend/device/opencl/opencl_array.py @@ -1,5 +1,3 @@ - - import numpy as np from hysop.tools.types import check_instance, first_not_None, to_tuple from hysop.tools.numpywrappers import slices_empty @@ -10,6 +8,7 @@ from hysop.core.arrays import MemoryType, MemoryOrdering from hysop.core.arrays import default_order from hysop.core.arrays.array import Array + class OpenClArray(Array): """ OpenCl memory array wrapper (pyopencl.array.Array). @@ -24,19 +23,19 @@ class OpenClArray(Array): """ if not isinstance(handle, clArray.Array): - msg='Handle should be a pyopencl.array.Array but got a {}.' - msg=msg.format(handle.__class__) + msg = 'Handle should be a pyopencl.array.Array but got a {}.' + msg = msg.format(handle.__class__) raise ValueError(msg) if not isinstance(backend, OpenClArrayBackend): - msg='Backend should be a OpenClArrayBackend but got a {}.' - msg=msg.format(handle.__class__) + msg = 'Backend should be a OpenClArrayBackend but got a {}.' + msg = msg.format(handle.__class__) raise ValueError(msg) - if handle.dtype in [np.float16, np.longdouble, np.bool]: - msg='{} unsupported yet for OpenCl arrays.'.format(handle.dtype) + if handle.dtype in [np.float16, np.longdouble, np.bool_]: + msg = '{} unsupported yet for OpenCl arrays.'.format(handle.dtype) raise TypeError(msg) - super(OpenClArray,self).__init__(handle=handle, backend=backend, **kargs) + super(OpenClArray, self).__init__(handle=handle, backend=backend, **kargs) # at this time the opencl backend works only with the default_queue # so we enforce it. @@ -62,68 +61,82 @@ class OpenClArray(Array): def get_ndim(self): return self._handle.ndim + def get_shape(self): return to_tuple(self._handle.shape) + def set_shape(self, shape): self._handle.shape = shape + def get_size(self): return self._handle.size + def get_strides(self): return self._handle.strides + def get_data(self): try: return self._handle.data except clArray.ArrayHasOffsetError: - offset = self.offset - alignment = self.backend.device.mem_base_addr_align - if (offset % alignment) == 0: - # try to return a subbuffer - try: - buf = self.base_data[offset:] - buf.__parent = self.base_data - return buf - except: - raise clArray.ArrayHasOffsetError - else: - raise + offset = self.offset + alignment = self.backend.device.mem_base_addr_align + if (offset % alignment) == 0: + # try to return a subbuffer + try: + buf = self.base_data[offset:] + buf.__parent = self.base_data + return buf + except: + raise clArray.ArrayHasOffsetError + else: + raise def get_base(self): return self._handle.base_data + def get_offset(self): return self._handle.offset + def get_dtype(self): return self._handle.dtype + def get_flags(self): return self._handle.flags + def get_T(self): return self.wrap(self._handle.T) + def get_imag(self): return self.backend.imag(self) + def get_real(self): return self.backend.real(self) + def get_nbytes(self): return self._handle.nbytes + def get_int_ptr(self): return self._handle.base_data.int_ptr + self.offset # array properties - ndim = property(get_ndim) - shape = property(get_shape, set_shape) - offset = property(get_offset) - strides = property(get_strides) - data = property(get_data) - base = property(get_base) - dtype = property(get_dtype) - flags = property(get_flags) - T = property(get_T) - imag = property(get_imag) - real = property(get_real) - size = property(get_size) - nbytes = property(get_nbytes) - int_ptr = property(get_int_ptr) + ndim = property(get_ndim) + shape = property(get_shape, set_shape) + offset = property(get_offset) + strides = property(get_strides) + data = property(get_data) + base = property(get_base) + dtype = property(get_dtype) + flags = property(get_flags) + T = property(get_T) + imag = property(get_imag) + real = property(get_real) + size = property(get_size) + nbytes = property(get_nbytes) + int_ptr = property(get_int_ptr) def get_base_data(self): return self._handle.base_data + def get_offset(self): return self._handle.offset base_data = property(get_base_data) @@ -135,26 +148,25 @@ class OpenClArray(Array): """ return clTools.dtype_to_ctype(self.dtype) - def get(self, handle=False, queue=None, ary=None): """ Returns a HostArray, view or copy of this array. """ queue = self.backend.check_queue(queue) - if self.size==0: + if self.size == 0: return None elif self.flags.forc: - host_array = self._call('get', queue=queue, ary=ary) + host_array = self._call('get', queue=queue, ary=ary) else: from hysop.backend.device.opencl.opencl_copy_kernel_launchers import \ - OpenClCopyBufferRectLauncher + OpenClCopyBufferRectLauncher if (ary is not None): host_array = ary else: host_array = self.backend.host_array_backend.empty_like(self) kl = OpenClCopyBufferRectLauncher.from_slices(varname='buffer', src=self, - dst=host_array) + dst=host_array) evt = kl(queue=queue) evt.wait() @@ -195,11 +207,13 @@ class OpenClArray(Array): Get the opencl context associated to this array. """ return self.backend.context + def get_device(self): """ Get the opencl device associated to this array. """ return self.backend.device + def set_default_queue(self, queue): """ Sets the default queue for this array. @@ -207,15 +221,17 @@ class OpenClArray(Array): # at this time the opencl backend works only with the default_queue # so we enforce it. if (queue is not self.default_queue): - msg='Default queue override has been disabled for non-default queues.' + msg = 'Default queue override has been disabled for non-default queues.' raise RuntimeError(msg) queue = self.backend.check_queue(queue) self._handle.queue = queue + def reset_default_queue(self): """ Resets the default queue for this array. """ self._handle.queue = None + def get_default_queue(self): """ Get the default queue for this array. @@ -223,7 +239,7 @@ class OpenClArray(Array): return self._handle.queue or self.backend.default_queue context = property(get_context) - device = property(get_device) + device = property(get_device) default_queue = property(get_default_queue, set_default_queue) def with_queue(queue): @@ -233,8 +249,8 @@ class OpenClArray(Array): queue = self.backend.check_queue(queue) yield self._call('with_queue', queue=queue) + # Array specific methods - ## Array specific methods def view(self, dtype=None): """ Returns view of array with the same data. If dtype is different from current dtype, @@ -251,8 +267,8 @@ class OpenClArray(Array): return self._call('reshape', *shape, order=order) def astype(self, dtype, queue=None, - order=MemoryOrdering.SAME_ORDER, - casting='unsafe', subok=True, copy=True): + order=MemoryOrdering.SAME_ORDER, + casting='unsafe', subok=True, copy=True): """ Copy of the array, cast to a specified type. """ @@ -263,10 +279,10 @@ class OpenClArray(Array): queue = self.backend.check_queue(queue) return self._call('astype', dtype=dtype, queue=queue) + # Cached kernels for efficiency - ## Cached kernels for efficiency def min(self, axis=None, out=None, - queue=None, synchronize=True, **kwds): + queue=None, synchronize=True, **kwds): """ Return the minimum along a given axis. On the first call, a kernel launcher is built for efficiency. @@ -274,8 +290,8 @@ class OpenClArray(Array): if (axis is None) and (out is None): if not hasattr(self, '_OpenClArray__min_launcher'): self.__min_launcher = self.backend.amin(a=self, axis=axis, out=out, - build_kernel_launcher=True, queue=queue, synchronize=False, - **kwds) + build_kernel_launcher=True, queue=queue, synchronize=False, + **kwds) evt = self.__min_launcher(queue=queue, synchronize=False) out = self.__min_launcher.out if synchronize: @@ -285,10 +301,10 @@ class OpenClArray(Array): return (evt, out) else: super(OpenClArray, self).min(self, axis=axis, out=out, - queue=queue, synchronize=synchronize, **kwds) + queue=queue, synchronize=synchronize, **kwds) def max(self, axis=None, out=None, - queue=None, synchronize=True, **kwds): + queue=None, synchronize=True, **kwds): """ Return the maximum along a given axis. On the first call, a kernel launcher is built for efficiency. @@ -296,8 +312,8 @@ class OpenClArray(Array): if (axis is None) and (out is None): if not hasattr(self, '_OpenClArray__max_launcher'): self.__max_launcher = self.backend.amax(a=self, axis=axis, out=out, - build_kernel_launcher=True, queue=queue, synchronize=False, - **kwds) + build_kernel_launcher=True, queue=queue, synchronize=False, + **kwds) evt = self.__max_launcher(queue=queue, synchronize=False) out = self.__max_launcher.out if synchronize: @@ -307,10 +323,10 @@ class OpenClArray(Array): return (evt, out) else: super(OpenClArray, self).max(self, axis=axis, out=out, - queue=queue, synchronize=synchronize, **kwds) + queue=queue, synchronize=synchronize, **kwds) def nanmin(self, axis=None, out=None, - queue=None, synchronize=True, **kwds): + queue=None, synchronize=True, **kwds): """ Return the minimum along a given axis. On the first call, a kernel launcher is built for efficiency. @@ -318,8 +334,8 @@ class OpenClArray(Array): if (axis is None) and (out is None): if not hasattr(self, '_OpenClArray__nanmin_launcher'): self.__nanmin_launcher = self.backend.nanmin(a=self, axis=axis, out=out, - build_kernel_launcher=True, queue=queue, synchronize=False, - **kwds) + build_kernel_launcher=True, queue=queue, synchronize=False, + **kwds) evt = self.__nanmin_launcher(queue=queue, synchronize=False) out = self.__nanmin_launcher.out if synchronize: @@ -329,10 +345,10 @@ class OpenClArray(Array): return (evt, out) else: super(OpenClArray, self).nanmin(self, axis=axis, out=out, - queue=queue, synchronize=synchronize, **kwds) + queue=queue, synchronize=synchronize, **kwds) def nanmax(self, axis=None, out=None, - queue=None, synchronize=True, **kwds): + queue=None, synchronize=True, **kwds): """ Return the maximum along a given axis. On the first call, a kernel launcher is built for efficiency. @@ -340,8 +356,8 @@ class OpenClArray(Array): if (axis is None) and (out is None): if not hasattr(self, '_OpenClArray__nanmax_launcher'): self.__nanmax_launcher = self.backend.nanmax(a=self, axis=axis, out=out, - build_kernel_launcher=True, queue=queue, synchronize=False, - **kwds) + build_kernel_launcher=True, queue=queue, synchronize=False, + **kwds) evt = self.__nanmax_launcher(queue=queue, synchronize=False) out = self.__nanmax_launcher.out if synchronize: @@ -351,10 +367,10 @@ class OpenClArray(Array): return (evt, out) else: super(OpenClArray, self).nanmax(self, axis=axis, out=out, - queue=queue, synchronize=synchronize, **kwds) + queue=queue, synchronize=synchronize, **kwds) def sum(self, axis=None, out=None, - queue=None, synchronize=True, **kwds): + queue=None, synchronize=True, **kwds): """ Return the sum along a given axis. On the first call, a kernel launcher is built for efficiency. @@ -362,8 +378,8 @@ class OpenClArray(Array): if (axis is None) and (out is None): if not hasattr(self, '_OpenClArray__sum_launcher'): self.__sum_launcher = self.backend.sum(a=self, axis=axis, out=out, - build_kernel_launcher=True, queue=queue, synchronize=False, - **kwds) + build_kernel_launcher=True, queue=queue, synchronize=False, + **kwds) evt = self.__sum_launcher(queue=queue, synchronize=False) out = self.__sum_launcher.out if synchronize: @@ -373,7 +389,7 @@ class OpenClArray(Array): return (evt, out) else: super(OpenClArray, self).sum(self, axis=axis, out=out, - queue=queue, synchronize=synchronize, **kwds) + queue=queue, synchronize=synchronize, **kwds) def setitem(self, subscript, value, queue=None): queue = first_not_None(queue, self.default_queue) @@ -384,21 +400,22 @@ class OpenClArray(Array): else: try: self.handle.setitem(subscript=subscript, value=value, - queue=queue) + queue=queue) except: from hysop.backend.device.opencl.opencl_copy_kernel_launchers import \ - OpenClCopyBufferRectLauncher + OpenClCopyBufferRectLauncher kl = OpenClCopyBufferRectLauncher.from_slices(varname='buffer', src=value, - dst=self, dst_slices=subscript) + dst=self, dst_slices=subscript) evt = kl(queue=queue) evt.wait() def __setitem__(self, subscript, value, **kwds): - if any( (s==0) for s in self[subscript].shape ): + if any((s == 0) for s in self[subscript].shape): return self.setitem(subscript=subscript, value=value, **kwds) def __str__(self): return str(self.get()) + def __repr__(self): return repr(self.get()) diff --git a/hysop/backend/device/opencl/opencl_array_backend.py b/hysop/backend/device/opencl/opencl_array_backend.py index 0bc155f9380f46cb1e8af17cbcaf38f4a210c119..2ea4948d89fb108c01e133ec8252e8353a67f987 100644 --- a/hysop/backend/device/opencl/opencl_array_backend.py +++ b/hysop/backend/device/opencl/opencl_array_backend.py @@ -1,30 +1,32 @@ -import os, re, warnings +import os +import re +import warnings import numpy as np from hysop import __KERNEL_DEBUG__ from hysop.tools.types import check_instance, to_tuple from hysop.tools.misc import prod from hysop.tools.numerics import is_complex, get_dtype, float_to_complex_dtype, \ - complex_to_float_dtype, find_common_dtype + complex_to_float_dtype, find_common_dtype from hysop.constants import Backend from hysop.constants import HYSOP_REAL, HYSOP_INTEGER, HYSOP_INDEX, HYSOP_BOOL from hysop.backend.device.opencl import cl, clArray, clTools, clRandom, \ - clReduction, clElementwise, clScan + clReduction, clElementwise, clScan from hysop.core.arrays import default_order from hysop.core.arrays import MemoryOrdering, MemoryType, QueuePolicy from hysop.core.arrays.array_backend import ArrayBackend -from hysop.core.arrays.array import Array +from hysop.core.arrays.array import Array # from hysop.core.memory.mempool import MemoryPool -from hysop.backend.device.opencl.opencl_allocator import OpenClAllocator +from hysop.backend.device.opencl.opencl_allocator import OpenClAllocator from hysop.backend.device.opencl.opencl_env import OpenClEnvironment from hysop.backend.device.opencl.opencl_kernel_launcher import OpenClKernelLauncherI, trace_kernel, profile_kernel from hysop.tools.numerics import is_fp, is_integer, is_signed, is_unsigned,\ - get_dtype, match_dtype + get_dtype, match_dtype from hysop.tools.types import first_not_None from hysop.backend.device.opencl.opencl_tools import get_or_create_opencl_env @@ -39,14 +41,16 @@ class _ElementwiseKernel(object): clElementwise.ElementwiseKernel adapted for use with OpenClArrayBackend.elementwise(...) """ + def __init__(self, context, arguments, operation, - name='elementwise', preamble='', options=[], - **kwds): + name='elementwise', preamble='', options=[], + **kwds): self.kernel = clElementwise.ElementwiseKernel(context=context, arguments=arguments, operation=operation, name=name, preamble=preamble, options=options) + def __call__(self, wait_for, args, **kwds): - return self.kernel.__call__(wait_for=wait_for, queue=kwds.get('queue',None), *args) + return self.kernel.__call__(wait_for=wait_for, queue=kwds.get('queue', None), *args) def to_kernel_launcher(self, name, wait_for, queue, args, **kwds): """Build an _OpenClElementWiseKernelLauncher from self.""" @@ -54,7 +58,8 @@ class _ElementwiseKernel(object): class OpenClElementwiseKernelLauncher(OpenClKernelLauncherI): """Utility class to build opencl reduction kernel launchers.""" __slots__ = ('_name', 'kernel', 'kernel_args', 'kernel_kwds', - 'default_queue') + 'default_queue') + def __init__(self, name, kernel, args, default_queue, **extra_kernel_kwds): super(OpenClElementwiseKernelLauncher, self).__init__(name=name) kernel_args = args @@ -63,9 +68,11 @@ class _ElementwiseKernel(object): self.kernel_args = kernel_args self.kernel_kwds = kernel_kwds self.default_queue = default_queue - self._apply_msg=' '+name+'<<<{}>>>()'.format(args[0].shape) + self._apply_msg = ' '+name+'<<<{}>>>()'.format(args[0].shape) + def global_size_configured(self): return True + def __call__(self, queue=None, wait_for=None, **kwds): trace_kernel(self._apply_msg) queue = first_not_None(queue, self.default_queue) @@ -77,7 +84,8 @@ class _ElementwiseKernel(object): return evt return OpenClElementwiseKernelLauncher(name=name, kernel=self.kernel, - args=args, default_queue=queue, **kwds) + args=args, default_queue=queue, **kwds) + class _ReductionKernel(object): """ @@ -105,22 +113,23 @@ class _ReductionKernel(object): clReduction.ReductionKernel adapted for use with OpenClArrayBackend.elementwise(...) """ + def __init__(self, context, arguments, reduce_expr, map_expr, neutral, dtype, - name='reduce', preamble='', options=[], - **kargs): + name='reduce', preamble='', options=[], + **kargs): # remove output argument from arguments list arguments = arguments.split(',') arguments = ','.join(arguments[1:]) self.kernel = clReduction.ReductionKernel(ctx=context, dtype_out=dtype, - neutral=neutral, - reduce_expr=reduce_expr, map_expr=map_expr, - arguments=arguments, - name=name, preamble=preamble, options=options) + neutral=neutral, + reduce_expr=reduce_expr, map_expr=map_expr, + arguments=arguments, + name=name, preamble=preamble, options=options) def __call__(self, wait_for, return_event, iargs, oargs, pargs, queue, **kargs): - assert len(oargs)==1 - assert len(iargs)>=1 + assert len(oargs) == 1 + assert len(iargs) >= 1 return self.kernel.__call__(*(iargs+pargs), out=oargs[0], queue=queue, wait_for=wait_for, return_event=return_event, allocator=None, @@ -132,20 +141,23 @@ class _ReductionKernel(object): class OpenClReductionKernelLauncher(OpenClKernelLauncherI): """Utility class to build opencl reduction kernel launchers.""" __slots__ = ('_name', 'kernel', 'kernel_args', 'kernel_kwds', - 'default_queue', 'return_event') + 'default_queue', 'return_event') + def __init__(self, name, kernel, return_event, - iargs, oargs, pargs, default_queue, **extra_kernel_kwds): + iargs, oargs, pargs, default_queue, **extra_kernel_kwds): super(OpenClReductionKernelLauncher, self).__init__(name=name) kernel_args = (iargs+pargs) kernel_kwds = dict(out=oargs[0], return_event=True, - allocator=None, range=None, slice=None) + allocator=None, range=None, slice=None) self.kernel = kernel self.kernel_args = kernel_args self.kernel_kwds = kernel_kwds self.default_queue = default_queue - self._apply_msg=' '+name+'<<<{}>>>()'.format(self.kernel_args[0].shape) + self._apply_msg = ' '+name+'<<<{}>>>()'.format(self.kernel_args[0].shape) + def global_size_configured(self): return True + def __call__(self, queue=None, wait_for=None, **kwds): trace_kernel(self._apply_msg) queue = first_not_None(queue, self.default_queue) @@ -158,7 +170,8 @@ class _ReductionKernel(object): return evt return OpenClReductionKernelLauncher(name=name, kernel=self.kernel, - default_queue=queue, **kwds) + default_queue=queue, **kwds) + class _GenericScanKernel(object): """ @@ -228,24 +241,25 @@ class _GenericScanKernel(object): clScan.GenericScanKernel adapted for use with OpenClArrayBackend.elementwise(...) """ + def __init__(self, context, dtype, arguments, - input_expr, scan_expr, neutral, - output_statement, - is_segment_start_expr=None, input_fetch_exprs=None, - name='generic_scan', options=[], preamble='', - devices=None, - index_dtype = np.int32, - **kargs): + input_expr, scan_expr, neutral, + output_statement, + is_segment_start_expr=None, input_fetch_exprs=None, + name='generic_scan', options=[], preamble='', + devices=None, + index_dtype=np.int32, + **kargs): input_fetch_exprs = first_not_None(input_fetch_exprs, []) self.kernel = clScan.GenericScanKernel(ctx=context, - dtype=dtype, index_dtype=index_dtype, - arguments=arguments, neutral=neutral, - scan_expr=scan_expr, input_expr=input_expr, output_statement=output_statement, - is_segment_start_expr=is_segment_start_expr, - input_fetch_exprs=input_fetch_exprs, - name_prefix=name, preamble=preamble, options=options) + dtype=dtype, index_dtype=index_dtype, + arguments=arguments, neutral=neutral, + scan_expr=scan_expr, input_expr=input_expr, output_statement=output_statement, + is_segment_start_expr=is_segment_start_expr, + input_fetch_exprs=input_fetch_exprs, + name_prefix=name, preamble=preamble, options=options) def __call__(self, args, queue, wait_for=None, size=None, **kargs): """ @@ -254,8 +268,7 @@ class _GenericScanKernel(object): If not given, this length is inferred from the first array argument passed. """ return self.kernel.__call__(*args, queue=queue, - size=size, wait_for=wait_for) - + size=size, wait_for=wait_for) class OpenClArrayBackend(ArrayBackend): @@ -281,38 +294,39 @@ class OpenClArrayBackend(ArrayBackend): def get_host_array_backend(self): return self._host_array_backend - host_array_backend=property(get_host_array_backend) + host_array_backend = property(get_host_array_backend) def short_description(self): return ':OpenClBackend: tag={}, cl_env={}, allocator={}, host_backend={}]'.format( - self.tag, - self.cl_env.tag, - self.allocator.full_tag, - self.host_array_backend.full_tag) + self.tag, + self.cl_env.tag, + self.allocator.full_tag, + self.host_array_backend.full_tag) def __eq__(self, other): if not other.__class__ == self.__class__: return NotImplemented - eq = (self._context is other._context) - eq &= (self._default_queue is other._default_queue) - eq &= (self._allocator is other._allocator) + eq = (self._context is other._context) + eq &= (self._default_queue is other._default_queue) + eq &= (self._allocator is other._allocator) eq &= (self.host_array_backend is other.host_array_backend) return eq + def __ne__(self, other): return not (self == other) + def __hash__(self): return id(self._context) ^ id(self._default_queue) ^ id(self.allocator) ^ id(self.host_array_backend) - __backends = {} @classmethod def get_or_create(cls, cl_env, - queue=None, - allocator=None, - host_array_backend=None): + queue=None, + allocator=None, + host_array_backend=None): from hysop.core.arrays.all import HostArrayBackend, \ - default_host_array_backend + default_host_array_backend host_array_backend = first_not_None(host_array_backend, default_host_array_backend) if (queue is None): queue = cl_env.default_queue @@ -322,7 +336,7 @@ class OpenClArrayBackend(ArrayBackend): return cls.__backends[key] else: obj = cls(cl_env=cl_env, queue=queue, allocator=allocator, - host_array_backend=host_array_backend) + host_array_backend=host_array_backend) cls.__backends[key] = obj return obj @@ -330,7 +344,7 @@ class OpenClArrayBackend(ArrayBackend): return super(OpenClArrayBackend, cls).__new__(cls, allocator=None) def __init__(self, cl_env=None, queue=None, allocator=None, - host_array_backend=None): + host_array_backend=None): """ Initialize and OpenClArrayBackend with OpenCL environment cl_env. @@ -350,27 +364,27 @@ class OpenClArrayBackend(ArrayBackend): check_instance(allocator, OpenClAllocator, allow_none=True) # disable multi queue support at this time - msg='Non-default queue support has been disabled. Please provide a cl_env only.' + msg = 'Non-default queue support has been disabled. Please provide a cl_env only.' if (cl_env is None): raise RuntimeError(msg) - #if (queue is not None) and (queue is not cl_env.default_queue): + # if (queue is not None) and (queue is not cl_env.default_queue): #raise RuntimeError(msg) if (queue is None): if (cl_env is None): cl_env = get_or_create_opencl_env() - context = cl_env.context - queue = cl_env.default_queue + context = cl_env.context + queue = cl_env.default_queue allocator = allocator or cl_env.allocator elif (cl_env is None): context = queue.context if (allocator is None): - msg='A custom allocator should be given as input ' - msg+='(cl_env was not specified but queue was).' + msg = 'A custom allocator should be given as input ' + msg += '(cl_env was not specified but queue was).' raise ValueError(msg) else: # cl_env and queue specified - context = cl_env.context + context = cl_env.context allocator = allocator or cl_env.allocator check_instance(allocator, OpenClAllocator) @@ -388,15 +402,15 @@ class OpenClArrayBackend(ArrayBackend): msg += '\n *Allocator context is {}.'.format(allocator.context) raise ValueError(msg) - assert len(context.devices)==1, 'Multidevice contexts are not supported.' + assert len(context.devices) == 1, 'Multidevice contexts are not supported.' from hysop.core.arrays.all import HostArrayBackend, \ - default_host_array_backend + default_host_array_backend host_array_backend = host_array_backend or default_host_array_backend check_instance(host_array_backend, HostArrayBackend) - super(OpenClArrayBackend,self).__init__(allocator=allocator) - self._context = context + super(OpenClArrayBackend, self).__init__(allocator=allocator) + self._context = context self._default_queue = queue self._host_array_backend = host_array_backend self._cl_env = cl_env @@ -406,10 +420,13 @@ class OpenClArrayBackend(ArrayBackend): def get_default_queue(self): return self._default_queue + def get_context(self): return self._context + def get_device(self): return self._context.devices[0] + def get_cl_env(self): return self._cl_env @@ -433,6 +450,8 @@ class OpenClArrayBackend(ArrayBackend): # BACKEND SPECIFIC METHODS # ############################ + + def can_wrap(self, handle): """ Should return True if handle is an Array or a array handle corresponding @@ -459,18 +478,17 @@ class OpenClArrayBackend(ArrayBackend): Prepare one argument for a call to pyopencl backend. """ if isinstance(arg, QueuePolicy): - if arg==QueuePolicy.COPY_QUEUE: + if arg == QueuePolicy.COPY_QUEUE: return clArray._copy_queue - elif arg==QueuePolicy.SAME_AS_TRANSFER: + elif arg == QueuePolicy.SAME_AS_TRANSFER: return clArray._same_as_transfer - elif arg==QueuePolicy.NO_QUEUE: + elif arg == QueuePolicy.NO_QUEUE: return None else: - msg='Unknown queue policy {}.'.format(arg) + msg = 'Unknown queue policy {}.'.format(arg) raise ValueError(msg) else: - return super(OpenClArrayBackend,self)._arg(arg) - + return super(OpenClArrayBackend, self)._arg(arg) def copyto(self, dst, src, queue=None, synchronize=True, **kargs): """ @@ -492,9 +510,9 @@ class OpenClArrayBackend(ArrayBackend): if isinstance(dst, OpenClArray): from hysop.backend.device.opencl.opencl_copy_kernel_launchers \ - import OpenClCopyBufferRectLauncher + import OpenClCopyBufferRectLauncher kl = OpenClCopyBufferRectLauncher.from_slices('buffer', - src=src, dst=dst) + src=src, dst=dst) evt = kl(queue=queue) elif isinstance(dst, Array): (_, evt) = src.handle.get_async(queue=queue, ary=dst.handle) @@ -522,7 +540,7 @@ class OpenClArrayBackend(ArrayBackend): if allow_none: pass else: - msg='Got None argument and allow_none is not set.' + msg = 'Got None argument and allow_none is not set.' raise ValueError(msg) elif np.isscalar(arg): dtype = get_dtype(arg) @@ -535,28 +553,28 @@ class OpenClArrayBackend(ArrayBackend): ctype = arg.ctype() argument = '__global {} {}* {}'.format(ctype, const, argname) elif isinstance(arg, Array): - msg='Argument \'{}\' is an Array but not an OpenClArray ' - msg+='(size={}, shape={}, dtype={}).' - msg=msg.format(argname, arg.size, arg.shape, arg.dtype) + msg = 'Argument \'{}\' is an Array but not an OpenClArray ' + msg += '(size={}, shape={}, dtype={}).' + msg = msg.format(argname, arg.size, arg.shape, arg.dtype) raise TypeError(msg) elif isinstance(arg, np.ndarray): - if arg.size==1: + if arg.size == 1: dtype = arg.dtype ctype = clTools.dtype_to_ctype(arg.dtype) argument = '{} {} {}'.format(const, ctype, argname) is_scalar = True else: - msg='Argument \'{}\' is a non-scalar np.ndarray (size={}, shape={}, dtype={}).' - msg=msg.format(argname, arg.size, arg.shape, arg.dtype) + msg = 'Argument \'{}\' is a non-scalar np.ndarray (size={}, shape={}, dtype={}).' + msg = msg.format(argname, arg.size, arg.shape, arg.dtype) raise TypeError(msg) else: - msg='Unknown argument type {} (value={}).'.format(arg.__class__, arg) + msg = 'Unknown argument type {} (value={}).'.format(arg.__class__, arg) raise TypeError(msg) return arg, argument, is_scalar, dtype def format_kernel_args(self, kargs, argprefix, argflags, - argcast=None, dtype=None, is_output=False, - arguments=None, const=False, filter_expr=None): + argcast=None, dtype=None, is_output=False, + arguments=None, const=False, filter_expr=None): if argflags is not None: assert 'has_fp16' in argflags @@ -570,16 +588,15 @@ class OpenClArrayBackend(ArrayBackend): assert not isinstance(argcast, set) and not isinstance(argcast, list) argcast = (argcast,)*len(kargs) else: - if len(argcast)!=len(kargs): + if len(argcast) != len(kargs): msg = 'Allocation dtypes input length mismatch:' - msg+='\n\tkargs: {}\n\targcast={}\n' - msg=msg.format(kargs, argcast) + msg += '\n\tkargs: {}\n\targcast={}\n' + msg = msg.format(kargs, argcast) raise ValueError(msg) - # find common floating point type fdtypes = [] - for i,arg in enumerate(kargs): + for i, arg in enumerate(kargs): if (argcast[i] == 'f') or (argcast[i] == 'c'): cast_dtype = match_dtype(arg, argcast[i]) if is_complex(cast_dtype): @@ -588,30 +605,30 @@ class OpenClArrayBackend(ArrayBackend): fdtypes.append(cast_dtype) if fdtypes: fcast_dtype = find_common_dtype(*tuple(fdtypes)) - #TODO fix np.float16 and np.longdouble support - if fcast_dtype==np.float16: - fcast_dtype=np.float32 - if fcast_dtype==np.longdouble: - fcast_dtype=np.float64 + # TODO fix np.float16 and np.longdouble support + if fcast_dtype == np.float16: + fcast_dtype = np.float32 + if fcast_dtype == np.longdouble: + fcast_dtype = np.float64 else: fcast_dtype = np.float64 ccast_dtype = float_to_complex_dtype(fcast_dtype) del fdtypes if (arguments is None): - arguments = [None,] * len(kargs) + arguments = [None, ] * len(kargs) else: self._check_argtype('format_kernel_args', 'arguments', arguments, list) - if len(kargs)!=len(arguments): + if len(kargs) != len(arguments): msg = 'Argument count mismatch:\n\tkargs: {}\n\targuments={}\n' - msg=msg.format(kargs, arguments) + msg = msg.format(kargs, arguments) raise ValueError(msg) kargs = list(kargs) - for i,arg in enumerate(kargs): - argname = '{}{}'.format(argprefix,i) + for i, arg in enumerate(kargs): + argname = '{}{}'.format(argprefix, i) arg, argument, is_scalar, dtype = self.format_kernel_arg(arg, - argname=argname, const=const) + argname=argname, const=const) kargs[i] = arg if (arguments[i] is None): arguments[i] = argument @@ -619,20 +636,20 @@ class OpenClArrayBackend(ArrayBackend): affectation = r'{}\[i\]\s*=\s*([\s\S]*?)\s*$'.format(argname) affectation = re.compile(affectation) is_cast = (argcast[i] is not None) - for (k,v) in filter_expr.items(): + for (k, v) in filter_expr.items(): expr = filter_expr[k] if expr is None: continue argi = argname+'[i]' - if is_output: #cast to nearest output value dtype + if is_output: # cast to nearest output value dtype if not is_complex(dtype): ctype = clTools.dtype_to_ctype(dtype) - convert = '{}=convert_{}_rte(\\1)'.format(argi,ctype) + convert = '{}=convert_{}_rte(\\1)'.format(argi, ctype) expr = expr.split(';') - for i,subexpr in enumerate(expr): - expr[i] = re.sub(affectation,convert,subexpr) + for i, subexpr in enumerate(expr): + expr[i] = re.sub(affectation, convert, subexpr) expr = ';'.join(expr) - elif is_cast: #input variable should be cast + elif is_cast: # input variable should be cast if (is_complex(dtype)) or (argcast[i] == 'c'): cast_dtype = ccast_dtype elif (is_fp(dtype)) or (argcast[i] == 'f'): @@ -645,20 +662,20 @@ class OpenClArrayBackend(ArrayBackend): ctype = clTools.dtype_to_ctype(ftype) if is_complex(dtype): argc = 'c{}_new({argi}.real,{argi}.imag)'.format(ctype, - argi=argi) + argi=argi) else: if dtype != ftype: argc = 'convert_{}({})'.format(ctype, argi) else: argc = argi - argc = 'c{}_fromreal({})'.format(ctype,argc) + argc = 'c{}_fromreal({})'.format(ctype, argc) expr = expr.replace(argi, argc) else: ctype = clTools.dtype_to_ctype(cast_dtype) argc = 'convert_{}({})'.format(ctype, argi) expr = expr.replace(argi, argc) if is_scalar: - expr = expr.replace(argi,argname) + expr = expr.replace(argi, argname) filter_expr[k] = expr if argflags is not None: if (dtype == np.float16): @@ -674,7 +691,7 @@ class OpenClArrayBackend(ArrayBackend): Return corresponding complex function name from pyopencl-complex.h matching handle complex datatype. """ - #see pyopencl/cl/pyopencl-complex.h + # see pyopencl/cl/pyopencl-complex.h assert is_complex(handle) if fname not in ['real', 'imag', 'abs', 'abs_squared', 'new', 'fromreal', @@ -683,20 +700,20 @@ class OpenClArrayBackend(ArrayBackend): 'divide', 'divider', 'pow', 'powr', 'rpow', 'sqrt', 'exp', 'log', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh']: - msg='The function \'{}\' has not been implemented for complex numbers, ' - msg+='see \'{}/cl/pyopencl-complex.h\' for more informations on ' - msg+='available methods.' + msg = 'The function \'{}\' has not been implemented for complex numbers, ' + msg += 'see \'{}/cl/pyopencl-complex.h\' for more informations on ' + msg += 'available methods.' msg = msg.format(fname, os.path.dirname(cl.__file__)) raise NotImplementedError(msg) dtype = get_dtype(handle) if dtype not in [np.complex64, np.complex128]: - msg='{} complex type has not been implemented yet.' - msg=msg.format(dtype) + msg = '{} complex type has not been implemented yet.' + msg = msg.format(dtype) raise NotImplementedError(msg) ftype = complex_to_float_dtype(dtype) ctype = clTools.dtype_to_ctype(ftype) - prefix='c{}_'.format(ctype) + prefix = 'c{}_'.format(ctype) return prefix+fname @@ -704,59 +721,58 @@ class OpenClArrayBackend(ArrayBackend): def binary_complex_fn(cls, fname, x0, x1): assert is_complex(x0) or is_complex(x1) if not is_complex(x0): - fname='r'+fname + fname = 'r'+fname elif not is_complex(x1): - fname=fname+'r' + fname = fname+'r' # rsub and subr do not exist in complex header if (fname == 'subr'): - operands_prefix=('','-') + operands_prefix = ('', '-') elif (fname == 'rsub'): - operands_prefix=('', cls.complex_fn('neg',x1)) + operands_prefix = ('', cls.complex_fn('neg', x1)) else: - operands_prefix=('','') + operands_prefix = ('', '') - ftype = find_common_dtype(x0,x1) + ftype = find_common_dtype(x0, x1) cmplx_fname = cls.complex_fn(fname, ftype) - expr='{}({}(x0[i]), {}(x1[i]))' + expr = '{}({}(x0[i]), {}(x1[i]))' expr = expr.format(cmplx_fname, *operands_prefix) # fix 0^0 and 0^x - if fname.find('pow')>=0: - default=expr - expr0='{}(1)'.format(cls.complex_fn('fromreal', ftype)) - expr1='{}(0)'.format(cls.complex_fn('fromreal', ftype)) - expr2='{}(NAN,NAN)'.format(cls.complex_fn('new', ftype)) - if fname=='rpow': - cond0='(x0[i]==0)' - cond1='((x1[i].real==0) && (x1[i].imag==0))' - elif fname=='powr': - cond0='((x0[i].real==0) && (x0[i].imag==0))' - cond1='(x1[i]==0)' - elif fname=='pow': - cond0='((x0[i].real==0) && (x0[i].imag==0))' - cond1='(x1[i].imag==0)' - cond2='(x1[i].real==0)' - expr='({cond0} ? ({cond1} ? ({cond2} ? {expr0} : {expr1}) : {expr2}) : {default})'.format( - cond0=cond0, cond1=cond1, cond2=cond2, - expr0=expr0, expr1=expr1, expr2=expr2, - default=default) + if fname.find('pow') >= 0: + default = expr + expr0 = '{}(1)'.format(cls.complex_fn('fromreal', ftype)) + expr1 = '{}(0)'.format(cls.complex_fn('fromreal', ftype)) + expr2 = '{}(NAN,NAN)'.format(cls.complex_fn('new', ftype)) + if fname == 'rpow': + cond0 = '(x0[i]==0)' + cond1 = '((x1[i].real==0) && (x1[i].imag==0))' + elif fname == 'powr': + cond0 = '((x0[i].real==0) && (x0[i].imag==0))' + cond1 = '(x1[i]==0)' + elif fname == 'pow': + cond0 = '((x0[i].real==0) && (x0[i].imag==0))' + cond1 = '(x1[i].imag==0)' + cond2 = '(x1[i].real==0)' + expr = '({cond0} ? ({cond1} ? ({cond2} ? {expr0} : {expr1}) : {expr2}) : {default})'.format( + cond0=cond0, cond1=cond1, cond2=cond2, + expr0=expr0, expr1=expr1, expr2=expr2, + default=default) expr = 'y0[i]={}'.format(expr) return expr - def elementwise(self, ikargs, okargs, extra_kargs=None, - input_arguments=None, output_arguments=None, extra_arguments=None, - filter_expr=None, - dtype=None, infer_dtype=False, - queue=None, infer_queue=False, - synchronize=True, convert_inputs=None, - allocator=None, alloc_shapes=None, alloc_dtypes=None, - Kernel=_ElementwiseKernel, - kernel_build_kwargs=None, kernel_call_kwargs=None, - build_kernel_launcher=False): + input_arguments=None, output_arguments=None, extra_arguments=None, + filter_expr=None, + dtype=None, infer_dtype=False, + queue=None, infer_queue=False, + synchronize=True, convert_inputs=None, + allocator=None, alloc_shapes=None, alloc_dtypes=None, + Kernel=_ElementwiseKernel, + kernel_build_kwargs=None, kernel_call_kwargs=None, + build_kernel_launcher=False): r""" Build and call kernel that takes: n read-only scalars or OpenClArray as input arguments xi, @@ -864,9 +880,9 @@ class OpenClArrayBackend(ArrayBackend): """ from hysop.core.arrays.all import OpenClArray - extra_kargs = first_not_None(extra_kargs, ()) + extra_kargs = first_not_None(extra_kargs, ()) kernel_build_kwargs = first_not_None(kernel_build_kwargs, {}) - kernel_call_kwargs = first_not_None(kernel_call_kwargs, {}) + kernel_call_kwargs = first_not_None(kernel_call_kwargs, {}) self._check_argtype('elementwise', 'ikargs', ikargs, tuple) self._check_argtype('elementwise', 'okargs', okargs, tuple) @@ -880,10 +896,10 @@ class OpenClArrayBackend(ArrayBackend): alloc_dtypes = (alloc_dtypes,)*len(okargs) else: self._check_argtype('elementwise', 'alloc_dtype', alloc_dtypes, tuple) - if len(alloc_dtypes)!=len(okargs): + if len(alloc_dtypes) != len(okargs): msg = 'Allocation dtypes input length mismatch:' - msg+='\n\tokargs: {}\n\talloc_dtypes={}\n' - msg=msg.format(okargs, alloc_dtypes) + msg += '\n\tokargs: {}\n\talloc_dtypes={}\n' + msg = msg.format(okargs, alloc_dtypes) raise ValueError(msg) alloc_shapes = alloc_shapes or (None,)*len(okargs) @@ -901,38 +917,38 @@ class OpenClArrayBackend(ArrayBackend): shape = arg.shape order = arg.order elif arg.shape != shape: - msg='{} with shape {} does not match reference shape {}.' - msg=msg.format(arg,args.shape,shape) + msg = '{} with shape {} does not match reference shape {}.' + msg = msg.format(arg, args.shape, shape) elif arg.order != order: - msg='{} with order {} does not match reference order {}.' - msg=msg.format(arg,args.order,order) + msg = '{} with order {} does not match reference order {}.' + msg = msg.format(arg, args.order, order) if (shape is None): - msg='No OpenClArray argument was found in ikargs and okargs.' + msg = 'No OpenClArray argument was found in ikargs and okargs.' raise ValueError(msg) if (queue is None): - msg='Queue has not been specified.' + msg = 'Queue has not been specified.' raise ValueError(msg) - alloc_dtypes = tuple([match_dtype(dtype,alloc_dtypes[i]) \ - for i in range(len(okargs))]) + alloc_dtypes = tuple([match_dtype(dtype, alloc_dtypes[i]) + for i in range(len(okargs))]) okargs = list(okargs) - for i, (arg, _shape, _dtype) in enumerate(zip(okargs,alloc_shapes,alloc_dtypes)): + for i, (arg, _shape, _dtype) in enumerate(zip(okargs, alloc_shapes, alloc_dtypes)): if (arg is None): alloc_shape = first_not_None(_shape, shape) alloc_dtype = first_not_None(_dtype, dtype) if (alloc_dtype is None): - msg='Output argument y{} has not been allocated and dtype was' - msg+=' not set (dtype={}, infer_dtype={}, alloc_dtypes={}).' - msg=msg.format(i,dtype,infer_dtype,alloc_dtypes) + msg = 'Output argument y{} has not been allocated and dtype was' + msg += ' not set (dtype={}, infer_dtype={}, alloc_dtypes={}).' + msg = msg.format(i, dtype, infer_dtype, alloc_dtypes) raise ValueError(msg) if (order is None): - msg='Output argument y{} has not been allocated and order was' - msg+=' not set.' - msg=msg.format(i) + msg = 'Output argument y{} has not been allocated and order was' + msg += ' not set.' + msg = msg.format(i) raise ValueError(msg) okargs[i] = self.empty(shape=alloc_shape, dtype=alloc_dtype, - queue=queue, order=order) + queue=queue, order=order) okargs = tuple(okargs) argflags = { @@ -941,22 +957,22 @@ class OpenClArrayBackend(ArrayBackend): } ikargs, input_arguments = \ - self.format_kernel_args(kargs=ikargs, argprefix='x', is_output=False, - arguments=input_arguments, filter_expr=filter_expr, - const=True, argflags=argflags, - argcast=convert_inputs, dtype=dtype) + self.format_kernel_args(kargs=ikargs, argprefix='x', is_output=False, + arguments=input_arguments, filter_expr=filter_expr, + const=True, argflags=argflags, + argcast=convert_inputs, dtype=dtype) okargs, output_arguments = \ - self.format_kernel_args(kargs=okargs, argprefix='y', is_output=True, - arguments=output_arguments, filter_expr=filter_expr, - const=False, argflags=argflags) + self.format_kernel_args(kargs=okargs, argprefix='y', is_output=True, + arguments=output_arguments, filter_expr=filter_expr, + const=False, argflags=argflags) extra_kargs, extra_arguments = \ - self.format_kernel_args(kargs=extra_kargs, argprefix='p', is_output=False, - arguments=extra_arguments, - const=True, argflags=argflags) + self.format_kernel_args(kargs=extra_kargs, argprefix='p', is_output=False, + arguments=extra_arguments, + const=True, argflags=argflags) - kargs = okargs + ikargs + extra_kargs + kargs = okargs + ikargs + extra_kargs arguments = output_arguments + input_arguments + extra_arguments arguments = ', '.join(arguments) @@ -964,19 +980,19 @@ class OpenClArrayBackend(ArrayBackend): assert not set(kernel_build_kwargs.keys()).intersection(filter_expr.keys()) kernel_build_kwargs.update(filter_expr) - blacklist = ['context', 'arguments','dtype','argflags'] + blacklist = ['context', 'arguments', 'dtype', 'argflags'] assert not set(kernel_build_kwargs.keys()).intersection(blacklist) - kernel_build_kwargs['context'] = queue.context + kernel_build_kwargs['context'] = queue.context kernel_build_kwargs['arguments'] = arguments - kernel_build_kwargs['dtype'] = dtype - kernel_build_kwargs['argflags'] = argflags + kernel_build_kwargs['dtype'] = dtype + kernel_build_kwargs['argflags'] = argflags # kernel call keyword arguments - blacklist = ['queue', 'args','iargs','oargs','pargs'] + blacklist = ['queue', 'args', 'iargs', 'oargs', 'pargs'] assert not set(kernel_call_kwargs.keys()).intersection(blacklist) kernel_call_kwargs['queue'] = queue - kernel_call_kwargs['args'] = kargs + kernel_call_kwargs['args'] = kargs kernel_call_kwargs['iargs'] = ikargs kernel_call_kwargs['oargs'] = okargs kernel_call_kwargs['pargs'] = extra_kargs @@ -989,33 +1005,34 @@ class OpenClArrayBackend(ArrayBackend): if val.__class__ in [list, tuple, set]: out = '{}[{}]'.format(val.__class__.__name__, ', '.join([val2str(v) for v in val])) - elif isinstance(val,Array): - out= '{}(shape={}, dtype={}, order={}, strides={}, offset={})' - out=out.format(val.__class__.__name__, val.shape, - val.dtype, val.order, val.strides, val.offset) + elif isinstance(val, Array): + out = '{}(shape={}, dtype={}, order={}, strides={}, offset={})' + out = out.format(val.__class__.__name__, val.shape, + val.dtype, val.order, val.strides, val.offset) elif val.__class__ in self._registered_backends().keys(): - out= '{}(shape={}, dtype={}, strides={})' - out=out.format(val.__class__.__name__, val.shape, - val.dtype, val.strides) + out = '{}(shape={}, dtype={}, strides={})' + out = out.format(val.__class__.__name__, val.shape, + val.dtype, val.strides) else: - out=val + out = val return out + def dic2str(dic): - entries=[] - for k,v in dic.items(): + entries = [] + for k, v in dic.items(): e = '{}: {}'.format(k, val2str(v)) entries.append(e) return '\n\t'+'\n\t'.join(entries) msg = 'Something went wrong during kernel build or kernel call:' - msg+= '\n build kargs:{}'.format(dic2str(kernel_build_kwargs)) - msg+= '\n call kargs:{}'.format(dic2str(kernel_call_kwargs)) - msg+= '\n' + msg += '\n build kargs:{}'.format(dic2str(kernel_build_kwargs)) + msg += '\n call kargs:{}'.format(dic2str(kernel_call_kwargs)) + msg += '\n' print(msg) raise if build_kernel_launcher: return knl.to_kernel_launcher(name=kernel_build_kwargs['name'], - **kernel_call_kwargs) + **kernel_call_kwargs) if isinstance(ret, tuple): evt = ret[-1] @@ -1026,12 +1043,12 @@ class OpenClArrayBackend(ArrayBackend): if synchronize: evt.wait() - if len(okargs)==1: + if len(okargs) == 1: return okargs[0] else: return okargs else: - if len(okargs)==1: + if len(okargs) == 1: return okargs[0], evt else: return okargs, evt @@ -1083,77 +1100,76 @@ class OpenClArrayBackend(ArrayBackend): 'operation': operation } return self.elementwise(ikargs=ikargs, okargs=okargs, - extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, output_arguments=output_arguments, - filter_expr=filter_expr, - dtype=dtype, infer_dtype=infer_dtype, - queue=queue, infer_queue=infer_queue, - synchronize=synchronize, - allocator=allocator, convert_inputs=convert_inputs, - alloc_shapes=alloc_shapes, alloc_dtypes=alloc_dtypes, - Kernel=Kernel, - kernel_build_kwargs= kernel_build_kwargs, - kernel_call_kwargs = kernel_call_kwargs, - build_kernel_launcher=build_kernel_launcher) + extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, output_arguments=output_arguments, + filter_expr=filter_expr, + dtype=dtype, infer_dtype=infer_dtype, + queue=queue, infer_queue=infer_queue, + synchronize=synchronize, + allocator=allocator, convert_inputs=convert_inputs, + alloc_shapes=alloc_shapes, alloc_dtypes=alloc_dtypes, + Kernel=Kernel, + kernel_build_kwargs=kernel_build_kwargs, + kernel_call_kwargs=kernel_call_kwargs, + build_kernel_launcher=build_kernel_launcher) def unary_op(self, x0, expr, out=None, - extra_kargs=None, extra_arguments=None, - input_arguments=None, output_arguments=None, - dtype=None, infer_dtype=True, - queue=None, infer_queue=True, - convert_inputs=None, - allocator=None, - alloc_shapes=None, alloc_dtypes=None, - synchronize=True, wait_for=None, - build_kernel_launcher=False, - name='unary_op', options=[], preamble=''): + extra_kargs=None, extra_arguments=None, + input_arguments=None, output_arguments=None, + dtype=None, infer_dtype=True, + queue=None, infer_queue=True, + convert_inputs=None, + allocator=None, + alloc_shapes=None, alloc_dtypes=None, + synchronize=True, wait_for=None, + build_kernel_launcher=False, + name='unary_op', options=[], preamble=''): return self.nary_op(ikargs=(x0,), okargs=(out,), operation=expr, - extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, output_arguments=output_arguments, - dtype=dtype, infer_dtype=infer_dtype, - queue=queue, infer_queue=infer_queue, - allocator=allocator, convert_inputs=convert_inputs, - alloc_shapes=alloc_shapes, alloc_dtypes=alloc_dtypes, - synchronize=synchronize, wait_for=wait_for, - build_kernel_launcher=build_kernel_launcher, - name=name, options=options, preamble=preamble) + extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, output_arguments=output_arguments, + dtype=dtype, infer_dtype=infer_dtype, + queue=queue, infer_queue=infer_queue, + allocator=allocator, convert_inputs=convert_inputs, + alloc_shapes=alloc_shapes, alloc_dtypes=alloc_dtypes, + synchronize=synchronize, wait_for=wait_for, + build_kernel_launcher=build_kernel_launcher, + name=name, options=options, preamble=preamble) def binary_op(self, x0, x1, expr, out=None, - extra_kargs=None, extra_arguments=None, - input_arguments=None, output_arguments=None, - dtype=None, infer_dtype=True, - queue=None, infer_queue=True, - convert_inputs=None, - allocator=None, - alloc_shapes=None, alloc_dtypes=None, - synchronize=True, wait_for=None, - build_kernel_launcher=False, - name='binary_op', options=[], preamble=''): - - return self.nary_op(ikargs=(x0,x1), okargs=(out,), operation=expr, - extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, output_arguments=output_arguments, - dtype=dtype, infer_dtype=infer_dtype, - queue=queue, infer_queue=infer_queue, - allocator=allocator, convert_inputs=convert_inputs, - alloc_shapes=alloc_shapes, alloc_dtypes=alloc_dtypes, - synchronize=synchronize, wait_for=wait_for, - build_kernel_launcher=build_kernel_launcher, - name=name, options=options, preamble=preamble) - + extra_kargs=None, extra_arguments=None, + input_arguments=None, output_arguments=None, + dtype=None, infer_dtype=True, + queue=None, infer_queue=True, + convert_inputs=None, + allocator=None, + alloc_shapes=None, alloc_dtypes=None, + synchronize=True, wait_for=None, + build_kernel_launcher=False, + name='binary_op', options=[], preamble=''): + + return self.nary_op(ikargs=(x0, x1), okargs=(out,), operation=expr, + extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, output_arguments=output_arguments, + dtype=dtype, infer_dtype=infer_dtype, + queue=queue, infer_queue=infer_queue, + allocator=allocator, convert_inputs=convert_inputs, + alloc_shapes=alloc_shapes, alloc_dtypes=alloc_dtypes, + synchronize=synchronize, wait_for=wait_for, + build_kernel_launcher=build_kernel_launcher, + name=name, options=options, preamble=preamble) def reduce(self, kargs, neutral, reduce_expr, map_expr='x0[i]', - axis=None, out=None, - extra_kargs=None, extra_arguments=None, - input_arguments=None, output_arguments=None, - convert_inputs=None, alloc_dtypes=None, - dtype=None, infer_dtype=True, - queue=None, infer_queue=True, - allocator=None, - synchronize=True, wait_for=None, - build_kernel_launcher=False, - name='reduction', options=[], preamble=''): + axis=None, out=None, + extra_kargs=None, extra_arguments=None, + input_arguments=None, output_arguments=None, + convert_inputs=None, alloc_dtypes=None, + dtype=None, infer_dtype=True, + queue=None, infer_queue=True, + allocator=None, + synchronize=True, wait_for=None, + build_kernel_launcher=False, + name='reduction', options=[], preamble=''): """ Reduce arrays elements over the whole array. kargs = kernel array or scalar arguments (tuple) @@ -1163,40 +1179,40 @@ class OpenClArrayBackend(ArrayBackend): if dtype is None: if any([is_signed(k) for k in kargs]): - dtype=np.int64 + dtype = np.int64 elif any([is_unsigned(k) for k in kargs]): - dtype=np.uint64 + dtype = np.uint64 else: pass - map_expr = map_expr.strip() + map_expr = map_expr.strip() reduce_expr = reduce_expr.strip() input_dtype = find_common_dtype(*kargs) if is_complex(input_dtype): map_ops = { - 'a+b': 'add', - 'a-b': 'sub', - 'a*b': 'mul', - } + 'a+b': 'add', + 'a-b': 'sub', + 'a*b': 'mul', + } if reduce_expr in map_ops: - neutral= '{}({})'.format(self.complex_fn('fromreal',input_dtype), neutral) - reduce_expr = '{}(a,b)'.format( self.complex_fn(map_ops[reduce_expr], input_dtype)) + neutral = '{}({})'.format(self.complex_fn('fromreal', input_dtype), neutral) + reduce_expr = '{}(a,b)'.format(self.complex_fn(map_ops[reduce_expr], input_dtype)) - self._unsupported_argument('reduce','axis',axis) + self._unsupported_argument('reduce', 'axis', axis) if (out is not None): self._check_argtype('reduce', 'out', out, OpenClArray) dtype = out.dtype - assert out.size==1 + assert out.size == 1 out = out.handle - Kernel=_ReductionKernel + Kernel = _ReductionKernel kernel_build_kwargs = { - 'name': name, - 'options': options, - 'preamble': preamble, - 'reduce_expr': reduce_expr, - 'neutral': neutral, + 'name': name, + 'options': options, + 'preamble': preamble, + 'reduce_expr': reduce_expr, + 'neutral': neutral, } kernel_call_kwargs = { 'return_event': True, @@ -1206,31 +1222,30 @@ class OpenClArrayBackend(ArrayBackend): 'map_expr': map_expr } return self.elementwise(ikargs=kargs, okargs=(out,), - extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, output_arguments=output_arguments, - filter_expr=filter_expr, - dtype=dtype, infer_dtype=infer_dtype, - queue=queue, infer_queue=infer_queue, - synchronize=synchronize, - convert_inputs=convert_inputs, alloc_dtypes=alloc_dtypes, - allocator=allocator, alloc_shapes=((1,),), - Kernel=Kernel, - build_kernel_launcher=build_kernel_launcher, - kernel_build_kwargs= kernel_build_kwargs, - kernel_call_kwargs = kernel_call_kwargs) - + extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, output_arguments=output_arguments, + filter_expr=filter_expr, + dtype=dtype, infer_dtype=infer_dtype, + queue=queue, infer_queue=infer_queue, + synchronize=synchronize, + convert_inputs=convert_inputs, alloc_dtypes=alloc_dtypes, + allocator=allocator, alloc_shapes=((1,),), + Kernel=Kernel, + build_kernel_launcher=build_kernel_launcher, + kernel_build_kwargs=kernel_build_kwargs, + kernel_call_kwargs=kernel_call_kwargs) def nanreduce(self, kargs, neutral, reduce_expr, map_expr='x0[i]', - axis=None, out=None, - extra_kargs=None, extra_arguments=None, - input_arguments=None, output_arguments=None, - dtype=None, infer_dtype=True, - queue=None, infer_queue=True, - allocator=None, - convert_inputs=None, - synchronize=True, wait_for=None, - build_kernel_launcher=False, - name='nan_reduction', options=[], preamble=''): + axis=None, out=None, + extra_kargs=None, extra_arguments=None, + input_arguments=None, output_arguments=None, + dtype=None, infer_dtype=True, + queue=None, infer_queue=True, + allocator=None, + convert_inputs=None, + synchronize=True, wait_for=None, + build_kernel_launcher=False, + name='nan_reduction', options=[], preamble=''): """ Reduce arrays elements over the whole array, replacing mapped expr NaNs by the neutral. dtype should be a floating point type. @@ -1240,7 +1255,7 @@ class OpenClArrayBackend(ArrayBackend): ctype = find_common_dtype(*kargs) nan_map_expr = '((isnan({expr}.real) || isnan({expr}.imag)) ? {neutral} : {expr})' nan_map_expr = nan_map_expr.format(expr=map_expr, - neutral='{}({})'.format(self.complex_fn('fromreal',ctype), neutral)) + neutral='{}({})'.format(self.complex_fn('fromreal', ctype), neutral)) elif any([is_fp(k) for k in kargs]): nan_map_expr = '(isnan({expr}) ? {neutral} : {expr})' nan_map_expr = nan_map_expr.format(expr=map_expr, neutral=neutral) @@ -1248,70 +1263,69 @@ class OpenClArrayBackend(ArrayBackend): nan_map_expr = map_expr return self.reduce(kargs=kargs, neutral=neutral, convert_inputs=convert_inputs, - reduce_expr=reduce_expr, map_expr=nan_map_expr, - axis=axis, out=out, extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, output_arguments=output_arguments, - dtype=dtype, infer_dtype=infer_dtype, queue=queue, infer_queue=infer_queue, - allocator=allocator, synchronize=synchronize, wait_for=wait_for, - build_kernel_launcher=build_kernel_launcher, - name=name, options=options, preamble=preamble) + reduce_expr=reduce_expr, map_expr=nan_map_expr, + axis=axis, out=out, extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, output_arguments=output_arguments, + dtype=dtype, infer_dtype=infer_dtype, queue=queue, infer_queue=infer_queue, + allocator=allocator, synchronize=synchronize, wait_for=wait_for, + build_kernel_launcher=build_kernel_launcher, + name=name, options=options, preamble=preamble) def generic_scan(self, kargs, neutral, scan_expr, output_statement, - input_expr='x0[i]', size=None, - is_segment_start_expr=None, input_fetch_exprs=None, - axis=None, out=None, - extra_kargs=None, extra_arguments=None, - input_arguments=None, output_arguments=None, - dtype=None, infer_dtype=True, - queue=None, infer_queue=True, - allocator=None, - convert_inputs=None, - synchronize=True, wait_for=None, - build_kernel_launcher=False, - name='generic_scan', options=[], preamble=''): + input_expr='x0[i]', size=None, + is_segment_start_expr=None, input_fetch_exprs=None, + axis=None, out=None, + extra_kargs=None, extra_arguments=None, + input_arguments=None, output_arguments=None, + dtype=None, infer_dtype=True, + queue=None, infer_queue=True, + allocator=None, + convert_inputs=None, + synchronize=True, wait_for=None, + build_kernel_launcher=False, + name='generic_scan', options=[], preamble=''): """ Scan arrays elements over the whole array. kargs = kernel array or scalar arguments (tuple) """ from hysop.core.arrays.all import OpenClArray - self._unsupported_argument('generic_scan','axis',axis) + self._unsupported_argument('generic_scan', 'axis', axis) if dtype is None: if any([is_signed(k) for k in kargs]): - dtype=np.int64 + dtype = np.int64 elif any([is_unsigned(k) for k in kargs]): - dtype=np.uint64 + dtype = np.uint64 # numpy does not force np.float16 / np.float32 to np.float64 - scan_expr = scan_expr.strip() input_dtype = find_common_dtype(*kargs) if is_complex(input_dtype): if input_dtype == np.complex128: - define='#define PYOPENCL_DEFINE_CDOUBLE' + define = '#define PYOPENCL_DEFINE_CDOUBLE' else: - define='' + define = '' include = '#include <pyopencl-complex.h>\n'+preamble - preamble='{}\n{}\n{}'.format(define,include,preamble) + preamble = '{}\n{}\n{}'.format(define, include, preamble) map_ops = { - 'a+b': 'add', - 'a-b': 'sub', - 'a*b': 'mul', - } + 'a+b': 'add', + 'a-b': 'sub', + 'a*b': 'mul', + } if scan_expr in map_ops: - neutral= '{}({})'.format(self.complex_fn('fromreal',input_dtype), neutral) - scan_expr = '{}(a,b)'.format( self.complex_fn(map_ops[scan_expr], input_dtype)) + neutral = '{}({})'.format(self.complex_fn('fromreal', input_dtype), neutral) + scan_expr = '{}(a,b)'.format(self.complex_fn(map_ops[scan_expr], input_dtype)) - Kernel=_GenericScanKernel + Kernel = _GenericScanKernel kernel_build_kwargs = { - 'name': name, - 'options': options, - 'preamble': preamble, - 'scan_expr': scan_expr, - 'neutral': neutral, - 'input_fetch_exprs': input_fetch_exprs + 'name': name, + 'options': options, + 'preamble': preamble, + 'scan_expr': scan_expr, + 'neutral': neutral, + 'input_fetch_exprs': input_fetch_exprs } kernel_call_kwargs = { @@ -1324,91 +1338,91 @@ class OpenClArrayBackend(ArrayBackend): 'is_segment_start_expr': is_segment_start_expr, } return self.elementwise(ikargs=kargs, okargs=(out,), - extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, output_arguments=output_arguments, - filter_expr=filter_expr, - dtype=dtype, infer_dtype=infer_dtype, - queue=queue, infer_queue=infer_queue, - synchronize=synchronize, - allocator=allocator, alloc_shapes=None, - convert_inputs=convert_inputs, - Kernel=Kernel, - build_kernel_launcher=build_kernel_launcher, - kernel_build_kwargs= kernel_build_kwargs, - kernel_call_kwargs = kernel_call_kwargs) + extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, output_arguments=output_arguments, + filter_expr=filter_expr, + dtype=dtype, infer_dtype=infer_dtype, + queue=queue, infer_queue=infer_queue, + synchronize=synchronize, + allocator=allocator, alloc_shapes=None, + convert_inputs=convert_inputs, + Kernel=Kernel, + build_kernel_launcher=build_kernel_launcher, + kernel_build_kwargs=kernel_build_kwargs, + kernel_call_kwargs=kernel_call_kwargs) def inclusive_scan(self, kargs, neutral, scan_expr, - input_expr='x0[i]', size=None, - axis=None, out=None, - extra_kargs=None, extra_arguments=None, - input_arguments=None, - dtype=None, infer_dtype=True, - queue=None, infer_queue=True, - allocator=None, - convert_inputs=None, - synchronize=True, wait_for=None, - build_kernel_launcher=False, - name='inclusive_scan', options=[], preamble=''): + input_expr='x0[i]', size=None, + axis=None, out=None, + extra_kargs=None, extra_arguments=None, + input_arguments=None, + dtype=None, infer_dtype=True, + queue=None, infer_queue=True, + allocator=None, + convert_inputs=None, + synchronize=True, wait_for=None, + build_kernel_launcher=False, + name='inclusive_scan', options=[], preamble=''): output_arguments = None output_statement = 'y0[i] = item' return self.generic_scan(kargs=kargs, neutral=neutral, scan_expr=scan_expr, - input_expr=input_expr, output_statement=output_statement, - size=size, axis=axis, out=out, - extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, output_arguments=output_arguments, - dtype=dtype, infer_dtype=infer_dtype, - queue=queue, infer_queue=infer_queue, - allocator=allocator, convert_inputs=convert_inputs, - synchronize=synchronize, wait_for=wait_for, - build_kernel_launcher=build_kernel_launcher, - name=name, options=options, preamble=preamble) + input_expr=input_expr, output_statement=output_statement, + size=size, axis=axis, out=out, + extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, output_arguments=output_arguments, + dtype=dtype, infer_dtype=infer_dtype, + queue=queue, infer_queue=infer_queue, + allocator=allocator, convert_inputs=convert_inputs, + synchronize=synchronize, wait_for=wait_for, + build_kernel_launcher=build_kernel_launcher, + name=name, options=options, preamble=preamble) def exclusive_scan(self, kargs, neutral, scan_expr, - input_expr='x0[i]', size=None, - axis=None, out=None, - extra_kargs=None, extra_arguments=None, - input_arguments=None, - dtype=None, infer_dtype=True, - queue=None, infer_queue=True, - allocator=None, - synchronize=True, wait_for=None, - build_kernel_launcher=False, - name='exclusive_scan', options=[], preamble=''): + input_expr='x0[i]', size=None, + axis=None, out=None, + extra_kargs=None, extra_arguments=None, + input_arguments=None, + dtype=None, infer_dtype=True, + queue=None, infer_queue=True, + allocator=None, + synchronize=True, wait_for=None, + build_kernel_launcher=False, + name='exclusive_scan', options=[], preamble=''): output_arguments = None output_statement = 'y0[i] = prev_item' return self.generic_scan(ikargs=kargs, neutral=neutral, scan_expr=scan_expr, - input_expr=input_expr, output_statement=output_statement, - size=size, axis=axis, out=out, - extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, output_arguments=output_arguments, - dtype=dtype, infer_dtype=infer_dtype, - queue=queue, infer_queue=infer_queue, - allocator=allocator, - synchronize=synchronize, wait_for=wait_for, - build_kernel_launcher=build_kernel_launcher, - name=name, options=options, preamble=preamble) + input_expr=input_expr, output_statement=output_statement, + size=size, axis=axis, out=out, + extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, output_arguments=output_arguments, + dtype=dtype, infer_dtype=infer_dtype, + queue=queue, infer_queue=infer_queue, + allocator=allocator, + synchronize=synchronize, wait_for=wait_for, + build_kernel_launcher=build_kernel_launcher, + name=name, options=options, preamble=preamble) def inclusive_nanscan(self, kargs, neutral, scan_expr, - input_expr='x0[i]', size=None, - axis=None, out=None, - extra_kargs=None, extra_arguments=None, - input_arguments=None, - dtype=None, infer_dtype=True, - queue=None, infer_queue=True, - allocator=None, - synchronize=True, wait_for=None, - build_kernel_launcher=False, - name='inclusive_nanscan', options=[], preamble=''): + input_expr='x0[i]', size=None, + axis=None, out=None, + extra_kargs=None, extra_arguments=None, + input_arguments=None, + dtype=None, infer_dtype=True, + queue=None, infer_queue=True, + allocator=None, + synchronize=True, wait_for=None, + build_kernel_launcher=False, + name='inclusive_nanscan', options=[], preamble=''): if any([is_complex(k) for k in kargs]): ctype = find_common_dtype(*kargs) nan_input_expr = '((isnan({expr}.real) || isnan({expr}.imag)) ? {neutral} : {expr})' nan_input_expr = nan_input_expr.format(expr=input_expr, - neutral='{}({})'.format(self.complex_fn('fromreal',ctype), neutral)) + neutral='{}({})'.format(self.complex_fn('fromreal', ctype), neutral)) elif any([is_fp(k) for k in kargs]): nan_input_expr = '(isnan({expr}) ? {neutral} : {expr})' nan_input_expr = nan_input_expr.format(expr=input_expr, neutral=neutral) @@ -1416,34 +1430,34 @@ class OpenClArrayBackend(ArrayBackend): nan_input_expr = input_expr return self.inclusive_scan(kargs=kargs, neutral=neutral, scan_expr=scan_expr, - input_expr=nan_input_expr, - size=size, axis=axis, out=out, - extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, - dtype=dtype, infer_dtype=infer_dtype, - queue=queue, infer_queue=infer_queue, - allocator=allocator, - synchronize=synchronize, wait_for=wait_for, - build_kernel_launcher=build_kernel_launcher, - name=name, options=options, preamble=preamble) + input_expr=nan_input_expr, + size=size, axis=axis, out=out, + extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, + dtype=dtype, infer_dtype=infer_dtype, + queue=queue, infer_queue=infer_queue, + allocator=allocator, + synchronize=synchronize, wait_for=wait_for, + build_kernel_launcher=build_kernel_launcher, + name=name, options=options, preamble=preamble) def exclusive_nanscan(self, kargs, neutral, scan_expr, - input_expr='x0[i]', size=None, - axis=None, out=None, - extra_kargs=None, extra_arguments=None, - input_arguments=None, - dtype=None, infer_dtype=True, - queue=None, infer_queue=True, - allocator=None, - synchronize=True, wait_for=None, - build_kernel_launcher=False, - name='exclusive_nanscan', options=[], preamble=''): + input_expr='x0[i]', size=None, + axis=None, out=None, + extra_kargs=None, extra_arguments=None, + input_arguments=None, + dtype=None, infer_dtype=True, + queue=None, infer_queue=True, + allocator=None, + synchronize=True, wait_for=None, + build_kernel_launcher=False, + name='exclusive_nanscan', options=[], preamble=''): if any([is_complex(k) for k in kargs]): ctype = find_common_dtype(*kargs) nan_input_expr = '((isnan({expr}.real) || isnan({expr}.imag)) ? {neutral} : {expr})' nan_input_expr = nan_input_expr.format(expr=input_expr, - neutral='{}({})'.format(self.complex_fn('fromreal',ctype), neutral)) + neutral='{}({})'.format(self.complex_fn('fromreal', ctype), neutral)) elif any([is_fp(k) for k in kargs]): nan_input_expr = '(isnan({expr}) ? {neutral} : {expr})' nan_input_expr = nan_input_expr.format(expr=input_expr, neutral=neutral) @@ -1451,54 +1465,54 @@ class OpenClArrayBackend(ArrayBackend): nan_input_expr = input_expr return self.exclusive_scan(kargs=kargs, neutral=neutral, scan_expr=scan_expr, - input_expr=nan_input_expr, - size=size, axis=axis, out=out, - extra_kargs=extra_kargs, extra_arguments=extra_arguments, - input_arguments=input_arguments, - dtype=dtype, infer_dtype=infer_dtype, - queue=queue, infer_queue=infer_queue, - allocator=allocator, - synchronize=synchronize, wait_for=wait_for, - build_kernel_launcher=build_kernel_launcher, - name=name, options=options, preamble=preamble) + input_expr=nan_input_expr, + size=size, axis=axis, out=out, + extra_kargs=extra_kargs, extra_arguments=extra_arguments, + input_arguments=input_arguments, + dtype=dtype, infer_dtype=infer_dtype, + queue=queue, infer_queue=infer_queue, + allocator=allocator, + synchronize=synchronize, wait_for=wait_for, + build_kernel_launcher=build_kernel_launcher, + name=name, options=options, preamble=preamble) def check_queue(self, queue): if (queue is None): return self.default_queue if queue.context != self.context: - msg='Given queue does not match backend context.' + msg = 'Given queue does not match backend context.' raise RuntimeError(msg) return queue ########################### # ARRAY CREATION ROUTINES # def array(self, shape, dtype=HYSOP_REAL, order=default_order, - queue=None, min_alignment=None, - buf=None, offset=0, - strides=None, events=None): + queue=None, min_alignment=None, + buf=None, offset=0, + strides=None, events=None): """ Create an OpenClArray, see pyopencl.array.Array constructor. If a queue is specified, it is set as default queue, else it will be backend.default_queue. """ - #FIXME OpenCL half float support + # FIXME OpenCL half float support if dtype == np.float16: - dtype=np.float32 - msg='OpenClArrayBackend promoted float16 to float32 in array allocation of shape {}.' - msg=msg.format(shape) - warnings.warn(RuntimeWarning(msg)) - #FIXME OpenCL long double support + dtype = np.float32 + msg = 'OpenClArrayBackend promoted float16 to float32 in array allocation of shape {}.' + msg = msg.format(shape) + # warnings.warn(RuntimeWarning(msg)) + # FIXME OpenCL long double support if dtype == np.longdouble: - msg='OpenClArrayBackend demoted {} to float64 in array allocation of shape {}.' - msg=msg.format(dtype, shape) + msg = 'OpenClArrayBackend demoted {} to float64 in array allocation of shape {}.' + msg = msg.format(dtype, shape) dtype = np.float64 - warnings.warn(RuntimeWarning(msg)) - #FIXME OpenCL bool support - if dtype == np.bool: - dtype=HYSOP_BOOL - msg='OpenClArrayBackend promoted np.bool to {} in array allocation of shape {}.' - msg=msg.format(dtype, shape) - warnings.warn(RuntimeWarning(msg)) + # warnings.warn(RuntimeWarning(msg)) + # FIXME OpenCL bool support + if dtype == np.bool_: + dtype = HYSOP_BOOL + msg = 'OpenClArrayBackend promoted np.bool_ to {} in array allocation of shape {}.' + msg = msg.format(dtype, shape) + # warnings.warn(RuntimeWarning(msg)) shape = to_tuple(shape) @@ -1508,36 +1522,35 @@ class OpenClArrayBackend(ArrayBackend): msg = 'pyopencl.Array has been created with non-default queue.' raise RuntimeError(msg) queue = self.check_queue(queue) - cq = queue #force default queue + cq = queue # force default queue if (buf is None): - assert offset==0 + assert offset == 0 assert strides is None allocator = self.allocator if (min_alignment is None) or (min_alignment < self.allocator.device.mem_base_addr_align): - alignment=1 + alignment = 1 dtype = np.dtype(dtype) size = int(prod(shape)*dtype.itemsize) # prod( shape=(,) ) will return 1.0, so we cast to int for scalars else: - (size,nbytes,alignment) = self.get_alignment_and_size(shape=shape, - dtype=dtype, min_alignment=min_alignment) + (size, nbytes, alignment) = self.get_alignment_and_size(shape=shape, + dtype=dtype, min_alignment=min_alignment) buf = allocator.allocate_aligned(size, alignment=alignment) - handle = self._call(clArray.Array, cq=cq, shape=shape, dtype=dtype, order=order, - allocator=None, data=buf, offset=offset, - strides=strides, events=events) + allocator=None, data=buf, offset=offset, + strides=strides, events=events) array = self.wrap(handle) return array def asarray(self, a, queue=None, synchronize=True, - dtype=None, order=default_order, array_queue=QueuePolicy.SAME_AS_TRANSFER): + dtype=None, order=default_order, array_queue=QueuePolicy.SAME_AS_TRANSFER): """ Convert the input to an OpenClArray. Queue is set as default queue. """ - from hysop.backend.device.opencl.opencl_array import Array, OpenClArray + from hysop.backend.device.opencl.opencl_array import Array, OpenClArray from hysop.backend.host.host_array import HostArray acls = a.__class__.__name__ queue = self.check_queue(queue) @@ -1549,8 +1562,8 @@ class OpenClArrayBackend(ArrayBackend): elif hasattr(a, 'dtype'): dtype = a.dtype else: - msg='Could not extract dtype from type {} and argument not set (dtype).' - msg=msg.format(acls) + msg = 'Could not extract dtype from type {} and argument not set (dtype).' + msg = msg.format(acls) raise ValueError(msg) if isinstance(a, OpenClArray): @@ -1564,10 +1577,10 @@ class OpenClArrayBackend(ArrayBackend): a = a.handle array = self._call(clArray.to_device, queue=queue, ary=a, - async_=(not synchronize), array_queue=array_queue) + async_=(not synchronize), array_queue=array_queue) else: - msg='Unknown type to convert from {}.' - msg=msg.format(acls) + msg = 'Unknown type to convert from {}.' + msg = msg.format(acls) raise TypeError(msg) return array.view(dtype) @@ -1576,7 +1589,7 @@ class OpenClArrayBackend(ArrayBackend): """ Return an array copy of the given object. """ - self._unsupported_argument('copy','order',order,MemoryOrdering.SAME_ORDER) + self._unsupported_argument('copy', 'order', order, MemoryOrdering.SAME_ORDER) queue = queue = self.check_queue(queue) return self._call(a.handle.copy, queue=queue) @@ -1595,7 +1608,7 @@ class OpenClArrayBackend(ArrayBackend): a, = self._kernel_generator.arrays_to_symbols(a) expr = Assignment(a, value) self._kernel_generator.elementwise_kernel('fill', expr, - queue=queue, call_only_once=True) + queue=queue, call_only_once=True) # Ones and zeros def empty(self, shape, dtype=HYSOP_REAL, order=default_order, queue=None, min_alignment=None): @@ -1614,21 +1627,22 @@ class OpenClArrayBackend(ArrayBackend): a = self.empty(shape=shape, dtype=dtype, order=order, queue=queue, min_alignment=min_alignment) self.fill(a=a, value=fill_value, queue=queue) return a + def zeros(self, shape, dtype=HYSOP_REAL, order=default_order, queue=None, min_alignment=None): """ Return a new array of given shape and type, filled with zeros. Queue is set as default queue. """ return self.full(shape=shape, dtype=dtype, order=order, queue=queue, - fill_value=0, min_alignment=min_alignment) + fill_value=0, min_alignment=min_alignment) + def ones(self, shape, dtype=HYSOP_REAL, order=default_order, queue=None, min_alignment=None): """ Return a new array of given shape and type, filled with ones. Queue is set as default queue. """ return self.full(shape=shape, dtype=dtype, order=order, queue=queue, - fill_value=1, min_alignment=min_alignment) - + fill_value=1, min_alignment=min_alignment) def empty_like(self, a, shape=None, dtype=None, order=None, subok=True, queue=None, min_alignment=None): """ @@ -1639,7 +1653,8 @@ class OpenClArrayBackend(ArrayBackend): shape = first_not_None(shape, a.shape) order = first_not_None(order, getattr(a, 'order', default_order)) return self.array(shape=shape, queue=queue, - dtype=dtype, order=order, min_alignment=min_alignment) + dtype=dtype, order=order, min_alignment=min_alignment) + def full_like(self, a, fill_value, dtype=None, order=None, subok=True, queue=None, min_alignment=None, shape=None): """ Return a new array with the same shape and type as a given array. @@ -1649,20 +1664,22 @@ class OpenClArrayBackend(ArrayBackend): queue=queue, min_alignment=min_alignment, shape=shape) self.fill(a, value=fill_value, queue=queue) return a + def zeros_like(self, a, dtype=None, order=None, subok=True, queue=None, min_alignment=None, shape=None): """ Return an array of zeros with the same shape and type as a given array. Queue is set as default queue. """ - return self.full_like(a=a,fill_value=0,dtype=dtype,order=order,subok=subok, - queue=queue, min_alignment=min_alignment, shape=shape) + return self.full_like(a=a, fill_value=0, dtype=dtype, order=order, subok=subok, + queue=queue, min_alignment=min_alignment, shape=shape) + def ones_like(self, a, dtype=None, order=None, subok=True, queue=None, min_alignment=None, shape=None): """ Return an array of ones with the same shape and type as a given array. Queue is set as default queue. """ - return self.full_like(a=a,fill_value=1,dtype=dtype,order=order,subok=subok, - queue=queue, min_alignment=min_alignment, shape=shape) + return self.full_like(a=a, fill_value=1, dtype=dtype, order=order, subok=subok, + queue=queue, min_alignment=min_alignment, shape=shape) def arange(self, *args, **kargs): """ @@ -1680,9 +1697,11 @@ class OpenClArrayBackend(ArrayBackend): ############################### # ARRAY MANIPULATION ROUTINES # -## See https://docs.scipy.org/doc/numpy/reference/routines.array-manipulation.html +# See https://docs.scipy.org/doc/numpy/reference/routines.array-manipulation.html + +# Changing array shape + -#Changing array shape def reshape(self, a, newshape, order=default_order, **kargs): """ Gives a new shape to an array without changing its data. @@ -1693,19 +1712,19 @@ class OpenClArrayBackend(ArrayBackend): """ Return a contiguous flattened array. """ - self._unsupported_argument('ravel','order',order,MemoryOrdering.SAME_ORDER) + self._unsupported_argument('ravel', 'order', order, MemoryOrdering.SAME_ORDER) return a._call('ravel') -#Changing number of dimensions +# Changing number of dimensions def squeeze(self, a, axis=None): """ Remove single-dimensional entries from the shape of an array. """ - self._unsupported_argument('squeeze','axis',axis,None) + self._unsupported_argument('squeeze', 'axis', axis, None) return a._call('squeeze') -#Transpose-like operations -## /!\ those functions can alter the logical transposition state /!\ +# Transpose-like operations +# /!\ those functions can alter the logical transposition state /!\ def transpose(self, a, axes=None): """ Permute the dimensions of an array (only array strides are permutated). @@ -1714,82 +1733,91 @@ class OpenClArrayBackend(ArrayBackend): # BINARY OPERATIONS # -## See https://docs.scipy.org/doc/numpy/reference/routines.bitwise.html +# See https://docs.scipy.org/doc/numpy/reference/routines.bitwise.html ##################### # Elementwise bit operations + + def bitwise_and(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Compute the bit-wise AND of two arrays element-wise. """ expr = 'y0[i] = (x0[i] & x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def bitwise_or(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Compute the bit-wise OR of two arrays element-wise. """ expr = 'y0[i] = (x0[i] | x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def bitwise_xor(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Compute the bit-wise XOR of two arrays element-wise. """ expr = 'y0[i] = (x0[i] ^ x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) def invert(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Compute bit-wise inversion, or bit-wise NOT, element-wise. """ expr = 'y0[i] = (~x0[i])' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def left_shift(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Shift the bits of an integer to the left. """ expr = 'y0[i] = (x0[i] << x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def right_shift(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Shift the bits of an integer to the right. """ expr = 'y0[i] = (x0[i] >> x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) # LOGIC FUNCTIONS # ################### -## See https://docs.scipy.org/doc/numpy/reference/routines.logic.html +# See https://docs.scipy.org/doc/numpy/reference/routines.logic.html + +# Truth value testing + -#Truth value testing def any(self, a, axis=None, out=None, queue=None, dtype=HYSOP_BOOL): """ Test whether any array elements along a given axis evaluate to True. """ return self.reduce(kargs=(a,), axis=axis, dtype=dtype, out=out, queue=queue, - neutral='false', reduce_expr='a||b') + neutral='false', reduce_expr='a||b') + def all(self, a, axis=None, out=None, queue=None, dtype=HYSOP_BOOL): """ Test whether all array elements along a given axis evaluate to True. """ return self.reduce(kargs=(a,), axis=axis, dtype=dtype, out=out, queue=queue, - neutral='true', reduce_expr='a&&b') + neutral='true', reduce_expr='a&&b') -#Array contents +# Array contents def isfinite(self, x, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Test element-wise for finiteness (not infinity or not Not a Number). """ @@ -1797,13 +1825,14 @@ class OpenClArrayBackend(ArrayBackend): expr = 'y0[i] = (!isinf(x0[i])) && (!isnan(x0[i]))' elif is_complex(x): expr = 'y0[i] = ((!isinf(x0[i].real)) && (!isnan(x0[i].real))' - expr+= '&& (!isinf(x0[i].imag)) && (!isnan(x0[i].imag)))' + expr += '&& (!isinf(x0[i].imag)) && (!isnan(x0[i].imag)))' else: expr = 'y0[i] = 1' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def isinf(self, x, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Test element-wise for positive or negative infinity. """ @@ -1814,9 +1843,10 @@ class OpenClArrayBackend(ArrayBackend): else: expr = 'y0[i] = 0' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def isnan(self, x, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Test element-wise for NaN and return result as a boolean array. """ @@ -1827,9 +1857,10 @@ class OpenClArrayBackend(ArrayBackend): else: expr = 'y0[i] = 0' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def isneginf(self, x, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Test element-wise for negative infinity, return result as bool array. """ @@ -1838,13 +1869,14 @@ class OpenClArrayBackend(ArrayBackend): expr = 'y0[i] = signbit(x0[i]) && isinf(x0[i])' elif is_complex(x): expr = 'y0[i] = ((signbit(x0[i].real) && isinf(x0[i].real))' - expr += '|| (signbit(x0[i].imag) && isinf(x0[i].imag)))' + expr += '|| (signbit(x0[i].imag) && isinf(x0[i].imag)))' else: expr = 'y0[i] = 0' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def isposinf(self, x, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Test element-wise for positive infinity, return result as bool array. """ @@ -1853,80 +1885,85 @@ class OpenClArrayBackend(ArrayBackend): expr = 'y0[i] = (!signbit(x0[i])) && isinf(x0[i])' elif is_complex(x): expr = 'y0[i] = ((!signbit(x0[i].real) && isinf(x0[i].real))' - expr += '|| (!signbit(x0[i].imag) && isinf(x0[i].imag)))' + expr += '|| (!signbit(x0[i].imag) && isinf(x0[i].imag)))' else: expr = 'y0[i] = 0' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) -#Logical operations +# Logical operations def logical_and(self, x1, x2, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Compute the truth value of x1 AND x2 element-wise. """ expr = 'y0[i] = (x0[i] && x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def logical_or(self, x1, x2, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Compute the truth value of x1 OR x2 element-wise. """ expr = 'y0[i] = (x0[i] || x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def logical_not(self, x, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Compute the truth value of NOT x element-wise. """ expr = 'y0[i] = (!x0[i])' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def logical_xor(self, x1, x2, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Compute the truth value of x1 XOR x2, element-wise. """ expr = 'y0[i] = (x0[i] ? (!x1[i]) : x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) -#Comparisson +# Comparisson def allequal(self, a, b, equal_nan=False, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Returns True if two arrays are element-wise equal within a tolerance. """ - x0,x1 = a,b - map_expr ='( x0[i]==x1[i] )' + x0, x1 = a, b + map_expr = '( x0[i]==x1[i] )' if is_fp(x0) and is_fp(x1) and equal_nan: map_expr += ' || ( isnan(x0[i]) && isnan(x1[i]) )' - return self.reduce(kargs=(x0,x1), neutral='true', reduce_expr='a&&b', - queue=queue, dtype=dtype, - map_expr=map_expr, - arguments=arguments) + return self.reduce(kargs=(x0, x1), neutral='true', reduce_expr='a&&b', + queue=queue, dtype=dtype, + map_expr=map_expr, + arguments=arguments) + def allclose(self, a, b, rtol=1e-05, atol=1e-08, equal_nan=False, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Returns True if two arrays are element-wise equal within a tolerance. """ if is_complex(a) or is_complex(b): map_expr = '({fabs}({sub}(x0[i],x1[i])) <= (atol + rtol*{fabs}(x1[i])))' - common_dtype = find_common_dtype(a,b) - map_expr = map_expr.format(fabs=self.complex_fn('abs',common_dtype), - sub=self.complex_fn('sub',common_dtype)) - convert_inputs='c' + common_dtype = find_common_dtype(a, b) + map_expr = map_expr.format(fabs=self.complex_fn('abs', common_dtype), + sub=self.complex_fn('sub', common_dtype)) + convert_inputs = 'c' else: map_expr = '(fabs(x0[i]-x1[i]) <= (atol + rtol*fabs(x1[i])))' - convert_inputs='f' - return self.reduce(kargs=(a,b), neutral='true', reduce_expr='a&&b', - queue=queue, dtype=dtype, - map_expr=map_expr, - extra_kargs=(np.float64(rtol), np.float64(atol)), - extra_arguments=['const double rtol', 'const double atol'], - convert_inputs=convert_inputs, alloc_dtypes='f') + convert_inputs = 'f' + return self.reduce(kargs=(a, b), neutral='true', reduce_expr='a&&b', + queue=queue, dtype=dtype, + map_expr=map_expr, + extra_kargs=(np.float64(rtol), np.float64(atol)), + extra_arguments=['const double rtol', 'const double atol'], + convert_inputs=convert_inputs, alloc_dtypes='f') + def isclose(self, a, b, rtol=1e-05, atol=1e-08, equal_nan=False, queue=None, dtype=HYSOP_BOOL): """ @@ -1934,50 +1971,51 @@ class OpenClArrayBackend(ArrayBackend): """ if is_complex(a) or is_complex(b): map_expr = '({fabs}({sub}(x0[i],x1[i])) <= (atol + rtol*{fabs}(x1[i])))' - common_dtype = find_common_dtype(a,b) - map_expr = map_expr.format(fabs=self.complex_fn('abs',common_dtype), - sub=self.complex_fn('sub',common_dtype)) - convert_inputs='c' + common_dtype = find_common_dtype(a, b) + map_expr = map_expr.format(fabs=self.complex_fn('abs', common_dtype), + sub=self.complex_fn('sub', common_dtype)) + convert_inputs = 'c' else: map_expr = '(fabs(x0[i]-x1[i]) <= (atol + rtol*fabs(x1[i])))' - convert_inputs='f' + convert_inputs = 'f' return self.binary_op(x0=a, x1=b, expr=expr, out=out, - extra_kargs=(np.float64(rtol), np.float64(atol)), - extra_arguments=['double rtol', 'double atol'], - queue=queue, dtype=dtype, - convert_inputs=convert_inputs) + extra_kargs=(np.float64(rtol), np.float64(atol)), + extra_arguments=['double rtol', 'double atol'], + queue=queue, dtype=dtype, + convert_inputs=convert_inputs) + def array_equal(self, a1, a2, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ True if two arrays have the same shape and elements, False otherwise. """ - return (a1.shape==a2.shape) and \ - self.all_equal(x1=a1,x2=a2,queue=queue,dtype=HYSOP_BOOL) + return (a1.shape == a2.shape) and \ + self.all_equal(x1=a1, x2=a2, queue=queue, dtype=HYSOP_BOOL) @staticmethod - def _make_comparisson_expr(x1,x2,comp): + def _make_comparisson_expr(x1, x2, comp): iseq = (comp == '==') expr = 'y0[i] = (({}) || (({}) && ({})))' if is_complex(x1) and is_complex(x2): expr = expr.format( - 'false' if iseq else 'x0[i].real {comp} x1[i].real', - 'x0[i].real == x1[i].real', - 'x0[i].imag {comp} x1[i].imag') + 'false' if iseq else 'x0[i].real {comp} x1[i].real', + 'x0[i].real == x1[i].real', + 'x0[i].imag {comp} x1[i].imag') elif is_complex(x1): expr = expr.format( - 'false' if iseq else 'x0[i].real {comp} x1[i]', - 'x0[i].real == x1[i]', - 'x0[i].imag {comp} 0') + 'false' if iseq else 'x0[i].real {comp} x1[i]', + 'x0[i].real == x1[i]', + 'x0[i].imag {comp} 0') elif is_complex(x2): expr = expr.format( - 'false' if iseq else 'x0[i] {comp} x1[i].real', - 'x0[i] == x1[i].real', - '0 {comp} x1[i].imag') + 'false' if iseq else 'x0[i] {comp} x1[i].real', + 'x0[i] == x1[i].real', + '0 {comp} x1[i].imag') else: expr = expr.format( - 'false' if iseq else 'x0[i] {comp} x1[i]', - 'x0[i] == x1[i]', - '0 {comp} 0') + 'false' if iseq else 'x0[i] {comp} x1[i]', + 'x0[i] == x1[i]', + '0 {comp} 0') return expr.format(comp=comp) def greater(self, x1, x2, out=None, @@ -1985,89 +2023,94 @@ class OpenClArrayBackend(ArrayBackend): """ Return the truth value of (x1 > x2) element-wise. """ - expr = self._make_comparisson_expr(x1,x2,'>') + expr = self._make_comparisson_expr(x1, x2, '>') return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def greater_equal(self, x1, x2, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Return the truth value of (x1 >= x2) element-wise. """ - expr = self._make_comparisson_expr(x1,x2,'>=') + expr = self._make_comparisson_expr(x1, x2, '>=') return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def less(self, x1, x2, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Return the truth value of (x1 < x2) element-wise. """ - expr = self._make_comparisson_expr(x1,x2,'<') + expr = self._make_comparisson_expr(x1, x2, '<') return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def less_equal(self, x1, x2, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Return the truth value of (x1 =< x2) element-wise. """ - expr = self._make_comparisson_expr(x1,x2,'<=') + expr = self._make_comparisson_expr(x1, x2, '<=') return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def equal(self, x1, x2, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Return (x1 == x2) element-wise. """ - expr = self._make_comparisson_expr(x1,x2,'==') + expr = self._make_comparisson_expr(x1, x2, '==') return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def not_equal(self, x1, x2, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Return (x1 != x2) element-wise. """ - expr = self._make_comparisson_expr(x1,x2,'!=') + expr = self._make_comparisson_expr(x1, x2, '!=') return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) # MATHEMATICAL FUNCTIONS # ########################## -## See https://docs.scipy.org/doc/numpy/reference/routines.math.html +# See https://docs.scipy.org/doc/numpy/reference/routines.math.html def _flt_or_cplx_unary_op(self, x, fname, **kargs): assert 'expr' not in kargs assert 'x0' not in kargs assert 'convert_inputs' not in kargs assert 'alloc_dtypes' not in kargs - expr='y0[i] = {}(x0[i])' + expr = 'y0[i] = {}(x0[i])' if is_complex(x): expr = expr.format(self.complex_fn(fname, x)) - convert_inputs='c' - alloc_dtypes='c' + convert_inputs = 'c' + alloc_dtypes = 'c' else: expr = expr.format(fname) - convert_inputs='f' - alloc_dtypes='f' + convert_inputs = 'f' + alloc_dtypes = 'f' return self.unary_op(expr=expr, x0=x, convert_inputs=convert_inputs, - alloc_dtypes=alloc_dtypes, **kargs) + alloc_dtypes=alloc_dtypes, **kargs) def _flt_or_map_cplx_unary_op(self, x, fname, **kargs): assert 'expr' not in kargs assert 'x0' not in kargs if is_complex(x): - expr='y0[i] = {}({fn}(x0[i].real), {fn}(x0[i].imag))' + expr = 'y0[i] = {}({fn}(x0[i].real), {fn}(x0[i].imag))' expr = expr.format(self.complex_fn('new', x), fn=fname) - _convert_inputs=None - _alloc_dtypes=(x.dtype,) + _convert_inputs = None + _alloc_dtypes = (x.dtype,) else: - expr='y0[i] = {}(x0[i])' + expr = 'y0[i] = {}(x0[i])' expr = expr.format(fname) - _convert_inputs='f' - _alloc_dtypes='f' + _convert_inputs = 'f' + _alloc_dtypes = 'f' alloc_dtypes = kargs.pop('alloc_dtypes', None) or _alloc_dtypes convert_inputs = kargs.pop('convert_inputs', None) or _convert_inputs return self.unary_op(expr=expr, x0=x, convert_inputs=convert_inputs, - alloc_dtypes=alloc_dtypes, **kargs) + alloc_dtypes=alloc_dtypes, **kargs) def _cplx_binary_op(self, x0, x1, fname, **kargs): assert is_complex(x0) or is_complex(x1) @@ -2075,96 +2118,105 @@ class OpenClArrayBackend(ArrayBackend): assert 'convert_inputs' not in kargs assert 'alloc_dtypes' not in kargs - convert_inputs='f' - alloc_dtypes='c' - expr = self.binary_complex_fn(fname,x0,x1) + convert_inputs = 'f' + alloc_dtypes = 'c' + expr = self.binary_complex_fn(fname, x0, x1) return self.binary_op(expr=expr, x0=x0, x1=x1, - convert_inputs=convert_inputs, - alloc_dtypes=alloc_dtypes, **kargs) + convert_inputs=convert_inputs, + alloc_dtypes=alloc_dtypes, **kargs) # Trigonometric functions + + def sin(self, x, out=None, queue=None, dtype=None): """ Trigonometric sine, element-wise. """ return self._flt_or_cplx_unary_op(x, 'sin', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def cos(self, x, out=None, queue=None, dtype=None): """ Cosine element-wise. """ return self._flt_or_cplx_unary_op(x, 'cos', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def tan(self, x, out=None, queue=None, dtype=None): """ Compute tangent element-wise. """ return self._flt_or_cplx_unary_op(x, 'tan', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) def arcsin(self, x, out=None, queue=None, dtype=None): """ Inverse sine, element-wise. """ return self._flt_or_cplx_unary_op(x, 'asin', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def arccos(self, x, out=None, queue=None, dtype=None): """ Trigonometric inverse cosine, element-wise. """ return self._flt_or_cplx_unary_op(x, 'acos', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def arctan(self, x, out=None, queue=None, dtype=None): """ Trigonometric inverse tangent, element-wise. """ return self._flt_or_cplx_unary_op(x, 'atan', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def arctan2(self, x1, x2, out=None, queue=None, dtype=None): """ Element-wise arc tangent of x1/x2 choosing the quadrant correctly. """ - expr='y0[i] = atan2(x0[i],x1[i])' + expr = 'y0[i] = atan2(x0[i],x1[i])' assert not is_complex(x1) and not is_complex(x2) return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') def hypot(self, x1, x2, out=None, queue=None, dtype=None): """ Given the legs of a right triangle, return its hypotenuse. """ assert not is_complex(x1) and not is_complex(x2) - expr='y0[i] = hypot(x0[i],x1[i])' + expr = 'y0[i] = hypot(x0[i],x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') - #def unwrap(self, p, discont=3.141592653589793, axis=-1): - #""" - #Unwrap by changing deltas between values to 2*pi complement. - #""" - #self.unary_op(x0=x, expr='y0[i] = unwrap(x0[i])', out=out, - #queue=queue, dtype=dtype, - # convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') + # def unwrap(self, p, discont=3.141592653589793, axis=-1): + # """ + # Unwrap by changing deltas between values to 2*pi complement. + # """ + # self.unary_op(x0=x, expr='y0[i] = unwrap(x0[i])', out=out, + #queue=queue, dtype=dtype, + # convert_inputs='f', alloc_dtypes='f') + def deg2rad(self, x, out=None, queue=None, dtype=None): """ Convert angles from degrees to radians. """ assert not is_complex(x) return self.unary_op(x0=x, expr='y0[i] = x0[i] * M_PI/180.0', out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') + def rad2deg(self, x, out=None, queue=None, dtype=None): """ Convert angles from radians to degrees. """ assert not is_complex(x) return self.unary_op(x0=x, expr='y0[i] = x0[i] * 180.0/M_PI', out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') # Hyperbolic functions def sinh(self, x, out=None, queue=None, dtype=None): @@ -2172,119 +2224,129 @@ class OpenClArrayBackend(ArrayBackend): Hyperbolic sine, element-wise. """ return self._flt_or_cplx_unary_op(x, 'sinh', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def cosh(self, x, out=None, queue=None, dtype=None): """ Hyperbolic cosine, element-wise. """ return self._flt_or_cplx_unary_op(x, 'cosh', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def tanh(self, x, out=None, queue=None, dtype=None): """ Compute hyperbolic tangent element-wise. """ return self._flt_or_cplx_unary_op(x, 'tanh', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) def arcsinh(self, x, out=None, queue=None, dtype=None): """ Inverse hyperbolic sine element-wise. """ return self._flt_or_cplx_unary_op(x, 'asinh', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def arccosh(self, x, out=None, queue=None, dtype=None): """ Inverse hyperbolic cosine, element-wise. """ return self._flt_or_cplx_unary_op(x, 'acosh', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def arctanh(self, x, out=None, queue=None, dtype=None): """ Inverse hyperbolic tangent element-wise. """ return self._flt_or_cplx_unary_op(x, 'atanh', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) # Rounding def around(self, a, decimals=0, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Round an array to the given number of decimals. """ - self._unsupported_argument('round','decimals',decimals,0) + self._unsupported_argument('round', 'decimals', decimals, 0) if is_complex(dtype): convert_inputs = (np.complex128,) else: convert_inputs = None - alloc_dtypes=(a.dtype,) + alloc_dtypes = (a.dtype,) return self._flt_or_map_cplx_unary_op(a, 'round', out=out, - queue=queue, dtype=dtype, - alloc_dtypes=alloc_dtypes, - convert_inputs=convert_inputs) + queue=queue, dtype=dtype, + alloc_dtypes=alloc_dtypes, + convert_inputs=convert_inputs) def fix(self, x, y=None, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Round to nearest integer towards zero. """ assert not is_complex(x) return self.unary_op(x0=x, expr='y0[i] = trunc(x0[i])', out=y, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') + def rint(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Round elements of the array to the nearest integer. """ return self._flt_or_map_cplx_unary_op(x, 'rint', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def floor(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the floor of the input, element-wise. """ assert not is_complex(x) if x.is_fp(): - expr='y0[i] = floor(x0[i])' + expr = 'y0[i] = floor(x0[i])' else: - expr='y0[i] = x0[i]' + expr = 'y0[i] = x0[i]' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') + def ceil(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the ceiling of the input, element-wise. """ assert not is_complex(x) return self.unary_op(x0=x, expr='y0[i] = ceil(x0[i])', out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') + def trunc(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the truncated value of the input, element-wise. """ assert not is_complex(x) return self.unary_op(x0=x, expr='y0[i] = trunc(x0[i])', out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') # Sums, product, differences + + def sum(self, a, axis=None, dtype=None, out=None, queue=None, **kwds): """ Sum of array elements over a given axis. """ return self.reduce(kargs=(a,), axis=axis, dtype=dtype, out=out, queue=queue, - neutral='0', reduce_expr='a+b', **kwds) + neutral='0', reduce_expr='a+b', **kwds) def prod(self, a, axis=None, dtype=None, out=None, queue=None): """ Return the product of array elements over a given axis. """ return self.reduce(kargs=(a,), axis=axis, dtype=dtype, out=out, queue=queue, - neutral='1', reduce_expr='a*b') + neutral='1', reduce_expr='a*b') def nansum(self, a, axis=None, dtype=None, out=None, queue=None, **kwds): """ @@ -2292,7 +2354,7 @@ class OpenClArrayBackend(ArrayBackend): as zeros. """ return self.nanreduce(kargs=(a,), axis=axis, dtype=dtype, out=out, queue=queue, - neutral='0', reduce_expr='a+b', **kwds) + neutral='0', reduce_expr='a+b', **kwds) def nanprod(self, a, axis=None, dtype=None, out=None, queue=None): """ @@ -2300,60 +2362,68 @@ class OpenClArrayBackend(ArrayBackend): Not a Numbers (NaNs) as ones. """ return self.nanreduce(kargs=(a,), axis=axis, dtype=dtype, out=out, queue=queue, - neutral='1', reduce_expr='a*b') + neutral='1', reduce_expr='a*b') def cumprod(self, a, axis=None, dtype=None, out=None, - queue=None): + queue=None): r""" Return the cumulative product of elements along a given axis. /!\ precision loss because of operation ordering """ return self.inclusive_scan(kargs=(a,), axis=axis, dtype=dtype, out=out, - neutral='1', scan_expr='a*b', queue=queue) + neutral='1', scan_expr='a*b', queue=queue) + def cumsum(self, a, axis=None, dtype=None, out=None, - queue=None): + queue=None): """ Return the cumulative sum of the elements along a given axis. """ return self.inclusive_scan(kargs=(a,), axis=axis, dtype=dtype, out=out, - neutral='0', scan_expr='a+b', queue=queue) + neutral='0', scan_expr='a+b', queue=queue) + def nancumprod(self, a, axis=None, dtype=None, out=None, - queue=None): + queue=None): r""" Return the cumulative product of array elements over a given axis treating Not a Numbers (NaNs) as one. /!\ precision loss because of operation ordering """ return self.inclusive_nanscan(kargs=(a,), axis=axis, dtype=dtype, out=out, - neutral='1', scan_expr='a*b', queue=queue) + neutral='1', scan_expr='a*b', queue=queue) + def nancumsum(self, a, axis=None, dtype=None, out=None, - queue=None): + queue=None): """ Return the cumulative sum of array elements over a given axis treating Not a Numbers (NaNs) as zero. """ return self.inclusive_nanscan(kargs=(a,), axis=axis, dtype=dtype, out=out, - neutral='0', scan_expr='a+b', queue=queue) + neutral='0', scan_expr='a+b', queue=queue) + def diff(self, a, n=1, axis=-1): """ Calculate the n-th discrete difference along given axis. """ self._not_implemented_yet('diff') + def ediff1d(self, ary, to_end=None, to_begin=None): """ The differences between consecutive elements of an array. """ self._not_implemented_yet('ediff1d') + def gradient(self, f, *varargs, **kwargs): """ Return the gradient of an N-dimensional array. """ self._not_implemented_yet('gradient') + def cross(self, a, b, axisa=-1, axisb=-1, axisc=-1, axis=None): """ Return the cross product of two (arrays of) vectors. """ self._not_implemented_yet('cross') + def trapz(self, y, x=None, dx=1.0, axis=-1): """ Integrate along the given axis using the composite trapezoidal rule. @@ -2366,205 +2436,221 @@ class OpenClArrayBackend(ArrayBackend): """ Calculate the exponential of all elements in the input array. """ - return self._flt_or_cplx_unary_op(x, 'exp',out=out, - queue=queue, dtype=dtype) + return self._flt_or_cplx_unary_op(x, 'exp', out=out, + queue=queue, dtype=dtype) + def exp2(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Calculate 2**p for all p in the input array. """ - expr='y0[i] = exp2(x0[i])' - return self._flt_or_cplx_unary_op(x, 'exp2',out=out, - queue=queue, dtype=dtype) + expr = 'y0[i] = exp2(x0[i])' + return self._flt_or_cplx_unary_op(x, 'exp2', out=out, + queue=queue, dtype=dtype) + def expm1(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Calculate exp(x) - 1 for all elements in the array. """ - expr='y0[i] = expm1(x0[i])' - return self._flt_or_cplx_unary_op(x, 'expm1',out=out, - queue=queue, dtype=dtype) + expr = 'y0[i] = expm1(x0[i])' + return self._flt_or_cplx_unary_op(x, 'expm1', out=out, + queue=queue, dtype=dtype) def log(self, x, out=None, queue=None, dtype=None): """ Natural logarithm, element-wise. """ - return self._flt_or_cplx_unary_op(x, 'log',out=out, - queue=queue, dtype=dtype) + return self._flt_or_cplx_unary_op(x, 'log', out=out, + queue=queue, dtype=dtype) + def log2(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Base-2 logarithm of x. """ - return self._flt_or_cplx_unary_op(x, 'log2',out=out, - queue=queue, dtype=dtype) + return self._flt_or_cplx_unary_op(x, 'log2', out=out, + queue=queue, dtype=dtype) + def log10(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the base 10 logarithm of the input array, element-wise. """ - return self._flt_or_cplx_unary_op(x, 'log10',out=out, - queue=queue, dtype=dtype) + return self._flt_or_cplx_unary_op(x, 'log10', out=out, + queue=queue, dtype=dtype) + def log1p(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the natural logarithm of one plus the input array, element-wise. """ - expr='y0[i] = log1p(x0[i])' - return self._flt_or_cplx_unary_op(x, 'log1p',out=out, - queue=queue, dtype=dtype) + expr = 'y0[i] = log1p(x0[i])' + return self._flt_or_cplx_unary_op(x, 'log1p', out=out, + queue=queue, dtype=dtype) def logaddexp(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Logarithm of the sum of exponentiations of the inputs. """ assert not is_complex(x1) and not is_complex(x2) - expr='y0[i] = log(exp(x0[i]) + exp(x1[i]))' + expr = 'y0[i] = log(exp(x0[i]) + exp(x1[i]))' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') + def logaddexp2(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Logarithm of the sum of exponentiations of the inputs in base-2. """ assert not is_complex(x1) and not is_complex(x2) - expr='y0[i] = log2(pow(2,x0[i]) + pow(2,x1[i]))' + expr = 'y0[i] = log2(pow(2,x0[i]) + pow(2,x1[i]))' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') - #Other special functions + # Other special functions def i0(self, x, out=None, queue=None, dtype=None): """ Modified Bessel function of the first kind, order 0. """ self._not_implemented_yet('i0', - convert_inputs='f', alloc_dtypes='f') + convert_inputs='f', alloc_dtypes='f') + def sinc(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the sinc function. """ assert not is_complex(x) - expr='y0[i] = sin(M_PI*x0[i]) / (M_PI*x0[i])' + expr = 'y0[i] = sin(M_PI*x0[i]) / (M_PI*x0[i])' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') - #Floating point routines + # Floating point routines def signbit(self, x, out=None, - queue=None, dtype=HYSOP_BOOL): + queue=None, dtype=HYSOP_BOOL): """ Returns element-wise True where signbit is set (less than zero). """ assert not is_complex(x) - expr='y0[i] = signbit(x0[i])' + expr = 'y0[i] = signbit(x0[i])' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f') + queue=queue, dtype=dtype, + convert_inputs='f') + def copysign(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Change the sign of x1 to that of x2, element-wise. """ assert not is_complex(x1) and not is_complex(x2) - expr='y0[i] = copysign(x0[i], x1[i])' + expr = 'y0[i] = copysign(x0[i], x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') + def frexp(self, x, out1=None, out2=None, - queue=None): + queue=None): """ Decompose the elements of x into mantissa and twos exponent. """ assert not is_complex(x) - expr='int buf; y0[i] = frexp(x0[i], &buf); y1[i]=buf;' - return self.nary_op(ikargs=(x,), okargs=(out1,out2), operation=expr, - queue=queue, - convert_inputs='f', alloc_dtypes=('f',np.int32)) + expr = 'int buf; y0[i] = frexp(x0[i], &buf); y1[i]=buf;' + return self.nary_op(ikargs=(x,), okargs=(out1, out2), operation=expr, + queue=queue, + convert_inputs='f', alloc_dtypes=('f', np.int32)) + def ldexp(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Returns x1 * 2**x2, element-wise. """ - expr='y0[i] = ldexp(x0[i])' + expr = 'y0[i] = ldexp(x0[i])' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') # Arithmetic operations + + def add(self, x1, x2, out=None, - queue=None, dtype=None, name='add',**kwds): + queue=None, dtype=None, name='add', **kwds): """ Add arguments element-wise. """ if is_complex(x1) or is_complex(x2): - return self._cplx_binary_op(x1,x2,'add', - out=out,queue=queue,dtype=dtype,name=name,**kwds) + return self._cplx_binary_op(x1, x2, 'add', + out=out, queue=queue, dtype=dtype, name=name, **kwds) else: expr = 'y0[i] = (x0[i] + x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype,name=name,**kwds) + queue=queue, dtype=dtype, name=name, **kwds) + def reciprocal(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the reciprocal of the argument, element-wise. """ dt = get_dtype(x) if is_complex(x): - expr = 'y0[i] = {}(1,x0[i])'.format(self.complex_fn('rdivide',x)) + expr = 'y0[i] = {}(1,x0[i])'.format(self.complex_fn('rdivide', x)) elif is_integer(x): info = np.iinfo(dt) - if (dt==np.int32) or (dt==np.int64): # to match numpy output - suffix = 'u' if is_unsigned(x) else '' - suffix += 'L' if (dt==np.int64) else '' - imin='{}{}'.format(info.min,suffix) - if dt==np.int64: - imin='{}{}'.format(info.min+1,suffix) #... + if (dt == np.int32) or (dt == np.int64): # to match numpy output + suffix = 'u' if is_unsigned(x) else '' + suffix += 'L' if (dt == np.int64) else '' + imin = '{}{}'.format(info.min, suffix) + if dt == np.int64: + imin = '{}{}'.format(info.min+1, suffix) # ... expr = 'y0[i] = (x0[i]==0 ? {imin} : (1{suffix}/x0[i]))' - expr = expr.format(imin=imin,suffix=suffix) + expr = expr.format(imin=imin, suffix=suffix) else: expr = 'y0[i] = (x0[i]==0 ? 0 : (1/x0[i]))' else: expr = 'y0[i] = (1.0/x0[i])' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def negative(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Numerical negative, element-wise. """ if is_complex(x): - expr = 'y0[i] = {}(x0[i])'.format(self.complex_fn('neg',x)) + expr = 'y0[i] = {}(x0[i])'.format(self.complex_fn('neg', x)) else: expr = 'y0[i] = (-x0[i])' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def multiply(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Multiply arguments element-wise. """ if is_complex(x1) or is_complex(x2): - return self._cplx_binary_op(x1,x2,'mul', - out=out,queue=queue,dtype=dtype,) + return self._cplx_binary_op(x1, x2, 'mul', + out=out, queue=queue, dtype=dtype,) else: expr = 'y0[i] = (x0[i] * x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def divide(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Divide arguments element-wise. """ if is_complex(x1) or is_complex(x2): - return self._cplx_binary_op(x1,x2,'divide', - out=out,queue=queue,dtype=dtype) + return self._cplx_binary_op(x1, x2, 'divide', + out=out, queue=queue, dtype=dtype) elif is_integer(x2): expr = 'y0[i] = (x1[i]==0 ? 0 : floor(x0[i] / x1[i]))' convert_inputs = np.float64 @@ -2572,53 +2658,57 @@ class OpenClArrayBackend(ArrayBackend): expr = 'y0[i] = (x0[i] / x1[i])' convert_inputs = None return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs=convert_inputs) + queue=queue, dtype=dtype, + convert_inputs=convert_inputs) + def power(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ First array elements raised to powers from second array, element-wise. """ if is_complex(x1) or is_complex(x2): - return self._cplx_binary_op(x1,x2,'pow', - out=out,queue=queue,dtype=dtype,) + return self._cplx_binary_op(x1, x2, 'pow', + out=out, queue=queue, dtype=dtype,) else: expr = 'pow(x0[i], x1[i])' - expr='y0[i] = ' + expr + expr = 'y0[i] = ' + expr return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f') + queue=queue, dtype=dtype, + convert_inputs='f') + def subtract(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Subtract arguments, element-wise. """ if is_complex(x1) or is_complex(x2): - return self._cplx_binary_op(x1,x2,'sub', - out=out,queue=queue,dtype=dtype,) + return self._cplx_binary_op(x1, x2, 'sub', + out=out, queue=queue, dtype=dtype,) else: expr = 'y0[i] = (x0[i] - x1[i])' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def true_divide(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Returns a true division of the inputs, element-wise. """ if is_complex(x1) or is_complex(x2): - return self._cplx_binary_op(x1,x2,'divide', - out=out,queue=queue,dtype=dtype) + return self._cplx_binary_op(x1, x2, 'divide', + out=out, queue=queue, dtype=dtype) else: expr = 'y0[i] = (x0[i] / x1[i])' - convert_inputs=None + convert_inputs = None if is_integer(x1) and is_integer(x2): - dtype=(dtype or HYSOP_REAL) - convert_inputs=dtype + dtype = (dtype or HYSOP_REAL) + convert_inputs = dtype return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs=convert_inputs) + queue=queue, dtype=dtype, + convert_inputs=convert_inputs) + def floor_divide(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the largest integer smaller or equal to the division of the inputs. Returns y = floor(x1/x2) @@ -2638,15 +2728,16 @@ class OpenClArrayBackend(ArrayBackend): self._not_implemented_yet('floor_divide.') elif is_integer(x1): expr = 'y0[i] = (x1[i]==0 ? 0 : floor(x0[i]/x1[i]))' - convert_inputs=(np.float64,None) + convert_inputs = (np.float64, None) else: expr = 'y0[i] = ((isnan(x0[i])||isnan(x1[i])||isinf(x0[i]))?NAN:(isinf(x1[i])?((signbit(x0[i])^signbit(x1[i]))?-1:0):floor(x0[i]/x1[i])))' - convert_inputs='f' + convert_inputs = 'f' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs=convert_inputs) + queue=queue, dtype=dtype, + convert_inputs=convert_inputs) + def fmod(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the element-wise remainder of division (REM). Remainder has the same sign as the dividend x1. @@ -2660,9 +2751,10 @@ class OpenClArrayBackend(ArrayBackend): else: expr = 'y0[i] = (x1[i] == 0 ? 0 : (x0[i] % x1[i]))' return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def mod(self, x1, x2, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return element-wise remainder of division (MOD). Remainder has the same sign as the divisor x2. @@ -2682,19 +2774,20 @@ class OpenClArrayBackend(ArrayBackend): """ assert not is_complex(x1) and not is_complex(x2) expr = 'x0[i] - x1[i]*floor(x0[i]/x1[i])' - convert_inputs=None + convert_inputs = None if is_integer(x1): expr = '(x1[i]==0 ? 0 : {})'.format(expr) - convert_inputs=(np.float64,None) + convert_inputs = (np.float64, None) if is_fp(x1) or is_fp(x2): expr = '(isnan(x0[i])||isnan(x1[i])?NAN:(isinf(x1[i])?(isinf(x0[i])?NAN:(signbit(x0[i])^signbit(x1[i])?x1[i]:x0[i])):{}))'.format(expr) - convert_inputs='f' - expr='y0[i] = '+expr + convert_inputs = 'f' + expr = 'y0[i] = '+expr return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs=convert_inputs) + queue=queue, dtype=dtype, + convert_inputs=convert_inputs) + def modf(self, x, out1=None, out2=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the fractional and integral parts of an array, element-wise. The fractional and integral parts are negative if the given number is negative. @@ -2707,12 +2800,12 @@ class OpenClArrayBackend(ArrayBackend): expr = 'y0[i] = modf(x0[i], &y1[i])' else: expr = 'y0[i] = 0; y1[i] = x0[i];' - return self.nary_op(ikargs=(x,), okargs=(out1,out2), operation=expr, - queue=queue, dtype=dtype, alloc_dtypes='f') + return self.nary_op(ikargs=(x,), okargs=(out1, out2), operation=expr, + queue=queue, dtype=dtype, alloc_dtypes='f') # Handling complex numbers def angle(self, z, deg=False, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the angle of the complex argument. """ @@ -2736,14 +2829,16 @@ class OpenClArrayBackend(ArrayBackend): expr = 'y0[i] = x0[i].real' dtype = dtype or complex_to_float_dtype(val.dtype) return self.unary_op(x0=val, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) else: return val + def imag(self, val): """ Return the imaginary part of the elements of the array. """ return self.wrap(val.handle.imag) + def conj(self, x, out=None): """ Return the complex conjugate, element-wise. @@ -2752,13 +2847,14 @@ class OpenClArrayBackend(ArrayBackend): # Miscellanous def convolve(self, a, v, mode='full', - queue=None, dtype=None): + queue=None, dtype=None): """ Returns the discrete, linear convolution of two one-dimensional sequences. """ self._not_implemented_yet('convolve') + def clip(self, a, a_min, a_max, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Clip (limit) the values in an array. """ @@ -2766,14 +2862,14 @@ class OpenClArrayBackend(ArrayBackend): expr = 'y0[i] = (isnan(x0[i]) ? NAN : clamp(x0[i], p0, p1))' elif is_complex(a): expr = 'y0[i] = ({cond0} ? {expr0} : ({cond1} ? {expr1} : {default}))' - cond0='({xr}<{p0r} || ({xr}=={p0r} && {xi}<{p0i}))' - cond1='({xr}>{p1r} || ({xr}=={p1r} && {xi}>{p1i}))' - expr0='p0' - expr1='p1' - default='x0[i]' + cond0 = '({xr}<{p0r} || ({xr}=={p0r} && {xi}<{p0i}))' + cond1 = '({xr}>{p1r} || ({xr}=={p1r} && {xi}>{p1i}))' + expr0 = 'p0' + expr1 = 'p1' + default = 'x0[i]' expr = expr.format(cond0=cond0, cond1=cond1, expr0=expr0, expr1=expr1, default=default) - expr = expr.format(xr = 'x0[i].real', p0r = 'p0.real', p1r='p1.real', - xi = 'x0[i].imag', p0i = 'p0.imag', p1i='p1.imag') + expr = expr.format(xr='x0[i].real', p0r='p0.real', p1r='p1.real', + xi='x0[i].imag', p0i='p0.imag', p1i='p1.imag') else: expr = 'y0[i] = clamp(x0[i], p0, p1)' atype = a.dtype @@ -2782,10 +2878,10 @@ class OpenClArrayBackend(ArrayBackend): a_max = np.asarray(a_max, dtype=atype) assert a_min <= a_max return self.unary_op(x0=a, expr=expr, out=out, extra_kargs=(a_min, a_max), - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) def clip_components(self, a, a_min, a_max, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Clip (limit) the real and imaginary part of a complex number independantly. ie: @@ -2801,39 +2897,42 @@ class OpenClArrayBackend(ArrayBackend): expr1 = 'y0[i].imag = (isnan(x0[i].imag) ? NAN : clamp(x0[i].imag, p0.imag, p1.imag));' expr = expr0+expr1 return self.unary_op(x0=a, expr=expr, out=out, extra_kargs=(a_min, a_max), - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) def sqrt(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the positive square-root of an array, element-wise. """ return self._flt_or_cplx_unary_op(x, 'sqrt', out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def cbrt(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the cube-root of an array, element-wise. """ assert not is_complex(x) expr = 'y0[i] = cbrt(x0[i])' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f', alloc_dtypes='f') + queue=queue, dtype=dtype, + convert_inputs='f', alloc_dtypes='f') + def square(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Return the element-wise square of the input. """ expr = 'y0[i] = x0[i]*x0[i]' if is_complex(x): - return self._cplx_binary_op(x,x,'mul', - queue=queue, dtype=dtype, out=out) + return self._cplx_binary_op(x, x, 'mul', + queue=queue, dtype=dtype, out=out) else: return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def nan_to_num(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Replace nan with zero and inf with finite numbers. """ @@ -2841,9 +2940,9 @@ class OpenClArrayBackend(ArrayBackend): if is_fp(x) or is_complex(x): dtype = dtype or x.dtype if is_complex(dtype): - ftype=complex_to_float_dtype(dtype) + ftype = complex_to_float_dtype(dtype) else: - ftype=dtype + ftype = dtype info = np.finfo(ftype) max_val = np.asarray(info.max, dtype=ftype) min_val = np.asarray(info.min, dtype=ftype) @@ -2853,14 +2952,15 @@ class OpenClArrayBackend(ArrayBackend): real = '(isnan(x0[i].real) ? 0 : (isinf(x0[i].real) ? (signbit(x0[i].real) ? p0 : p1) : x0[i].real))' imag = '(isnan(x0[i].imag) ? 0 : (isinf(x0[i].imag) ? (signbit(x0[i].imag) ? p0 : p1) : x0[i].imag))' expr = 'y0[i] = {new}({real},{imag})'.format(real=real, imag=imag, - new=self.complex_fn('new', dtype)) + new=self.complex_fn('new', dtype)) return self.unary_op(x0=x, expr=expr, out=out, - extra_kargs=(min_val, max_val), - queue=queue, dtype=dtype) + extra_kargs=(min_val, max_val), + queue=queue, dtype=dtype) elif out: out.copyfrom(x) else: return x.astype(dtype=dtype or x.dtype, queue=queue) + def real_if_close(self, a, tol=100, queue=None): """ If complex input returns a real array if complex parts are close to zero. @@ -2872,10 +2972,10 @@ class OpenClArrayBackend(ArrayBackend): tol = f.eps * tol if self.reduce(kargs=(a,), neutral='true', reduce_expr='a&&b', - map_expr='(fabs(x0[i].imag) < tol)', - queue=queue, dtype=HYSOP_BOOL, - extra_kargs=(np.float64(tol),), - extra_arguments=['const double tol']).get(): + map_expr='(fabs(x0[i].imag) < tol)', + queue=queue, dtype=HYSOP_BOOL, + extra_kargs=(np.float64(tol),), + extra_arguments=['const double tol']).get(): return a.real else: return a @@ -2896,28 +2996,29 @@ class OpenClArrayBackend(ArrayBackend): elif is_complex(x1) and is_complex(x2): default = '(((x0[i].real>x1[i].real) || ((x0[i].real==x1[i].real) && (x0[i].imag>x1[i].imag))) ? x0[i] : x1[i])' expr = '({} ? {} : ({} ? {} : {}))'.format( - '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x0[i]', - '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x1[i]', - default) + '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x0[i]', + '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x1[i]', + default) elif is_complex(x1): - fromreal=self.complex_fn('fromreal', x1) + fromreal = self.complex_fn('fromreal', x1) default = '(((x0[i].real>x1[i]) || ((x0[i].real==x1[i]) && (x0[i].imag>0))) ? x0[i] : {}(x1[i])'.format(fromreal) expr = '({} ? {} : ({} ? {} : {}))'.format( - '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x0[i]', - 'isnan(x1[i])', '{}(NAN)'.format(fromreal), - default) + '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x0[i]', + 'isnan(x1[i])', '{}(NAN)'.format(fromreal), + default) elif is_complex(x2): - fromreal=self.complex_fn('fromreal', x2) + fromreal = self.complex_fn('fromreal', x2) default = '(((x0[i]>x1[i].real) || ((x0[i]==x1[i].real) && (0>x1[i].imag))) ? {}(x0[i]) : x1[i])'.format(fromreal) expr = '({} ? {} : ({} ? {} : {}))'.format( - 'isnan(x0[i])', '{}(x0[i])'.format(fromreal), - '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x1[i]', - default) + 'isnan(x0[i])', '{}(x0[i])'.format(fromreal), + '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x1[i]', + default) else: expr = 'max(x0[i], x1[i])' - expr='y0[i] = '+expr + expr = 'y0[i] = '+expr return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def minimum(self, x1, x2, out=None, queue=None, dtype=None): """ @@ -2932,30 +3033,31 @@ class OpenClArrayBackend(ArrayBackend): elif is_complex(x1) and is_complex(x2): default = '(((x0[i].real<x1[i].real) || ((x0[i].real==x1[i].real) && (x0[i].imag<x1[i].imag))) ? x0[i] : x1[i])' expr = '({} ? {} : ({} ? {} : {}))'.format( - '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x0[i]', - '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x1[i]', - default) + '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x0[i]', + '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x1[i]', + default) elif is_complex(x1): - fromreal=self.complex_fn('fromreal', x1) + fromreal = self.complex_fn('fromreal', x1) default = '(((x0[i].real<x1[i]) || ((x0[i].real==x1[i]) && (x0[i].imag<0))) ? x0[i] : {}(x1[i])'.format(fromreal) expr = '({} ? {} : ({} ? {} : {}))'.format( - '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x0[i]', - 'isnan(x1[i])', '{}(x1[i])'.format(fromreal), - default) + '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x0[i]', + 'isnan(x1[i])', '{}(x1[i])'.format(fromreal), + default) elif is_complex(x2): - fromreal=self.complex_fn('fromreal', x2) + fromreal = self.complex_fn('fromreal', x2) default = '(((x0[i]<x1[i].real) || ((x0[i]==x1[i].real) && (0<x1[i].imag))) ? {}(x0[i]) : x1[i])'.format(fromreal) expr = '({} ? {} : ({} ? {} : {}))'.format( - 'isnan(x0[i])', '{}(x0[i])'.format(fromreal), - '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x1[i]', - default) + 'isnan(x0[i])', '{}(x0[i])'.format(fromreal), + '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x1[i]', + default) else: expr = 'min(x0[i], x1[i])' - expr='y0[i] = '+expr + expr = 'y0[i] = '+expr return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def fmax(self, x1, x2, out=None, - queue=None, dtype=None, name='_fmax'): + queue=None, dtype=None, name='_fmax'): """ Element-wise maximum of array elements, ignoring NaNs. """ @@ -2968,30 +3070,31 @@ class OpenClArrayBackend(ArrayBackend): elif is_complex(x1) and is_complex(x2): default = '(((x0[i].real>x1[i].real) || ((x0[i].real==x1[i].real) && (x0[i].imag>x1[i].imag))) ? x0[i] : x1[i])' expr = '({} ? {} : ({} ? {} : {}))'.format( - '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x1[i]', - '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x0[i]', - default) + '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x1[i]', + '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x0[i]', + default) elif is_complex(x1): - fromreal=self.complex_fn('fromreal', x1) + fromreal = self.complex_fn('fromreal', x1) default = '(((x0[i].real>x1[i]) || ((x0[i].real==x1[i]) && (x0[i].imag>0))) ? x0[i] : {}(x1[i])'.format(fromreal) expr = '({} ? {} : ({} ? {} : {}))'.format( - '(isnan(x0[i].real) || isnan(x0[i].imag))', '{}(x1[i])'.format(fromreal), - 'isnan(x1[i])', 'x0[i]', - default) + '(isnan(x0[i].real) || isnan(x0[i].imag))', '{}(x1[i])'.format(fromreal), + 'isnan(x1[i])', 'x0[i]', + default) elif is_complex(x2): - fromreal=self.complex_fn('fromreal', x2) + fromreal = self.complex_fn('fromreal', x2) default = '(((x0[i]>x1[i].real) || ((x0[i]==x1[i].real) && (0>x1[i].imag))) ? {}(x0[i]) : x1[i])'.format(fromreal) expr = '({} ? {} : ({} ? {} : {}))'.format( - 'isnan(x0[i])', 'x1[i]', - '(isnan(x1[i].real) || isnan(x1[i].imag))', '{}(x0[i])'.format(fromreal), - default) + 'isnan(x0[i])', 'x1[i]', + '(isnan(x1[i].real) || isnan(x1[i].imag))', '{}(x0[i])'.format(fromreal), + default) else: expr = 'max(x0[i], x1[i])' expr = 'y0[i] = ' + expr return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype) + queue=queue, dtype=dtype) + def fmin(self, x1, x2, out=None, - queue=None, dtype=None, name='_fmin', **kwds): + queue=None, dtype=None, name='_fmin', **kwds): """ Element-wise minimum of array elements, ignoring NaNs. """ @@ -3004,30 +3107,31 @@ class OpenClArrayBackend(ArrayBackend): elif is_complex(x1) and is_complex(x2): default = '(((x0[i].real<x1[i].real) || ((x0[i].real==x1[i].real) && (x0[i].imag<x1[i].imag))) ? x0[i] : x1[i])' expr = '({} ? {} : ({} ? {} : {}))'.format( - '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x1[i]', - '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x0[i]', - default) + '(isnan(x0[i].real) || isnan(x0[i].imag))', 'x1[i]', + '(isnan(x1[i].real) || isnan(x1[i].imag))', 'x0[i]', + default) elif is_complex(x1): - fromreal=self.complex_fn('fromreal', x1) + fromreal = self.complex_fn('fromreal', x1) default = '(((x0[i].real<x1[i]) || ((x0[i].real==x1[i]) && (x0[i].imag<0))) ? x0[i] : {}(x1[i])'.format(fromreal) expr = '({} ? {} : ({} ? {} : {}))'.format( - '(isnan(x0[i].real) || isnan(x0[i].imag))', '{}(x1[i])'.format(fromreal), - 'isnan(x1[i])', 'x0[i]', - default) + '(isnan(x0[i].real) || isnan(x0[i].imag))', '{}(x1[i])'.format(fromreal), + 'isnan(x1[i])', 'x0[i]', + default) elif is_complex(x2): - fromreal=self.complex_fn('fromreal', x2) + fromreal = self.complex_fn('fromreal', x2) default = '(((x0[i]<x1[i].real) || ((x0[i]==x1[i].real) && (0<x1[i].imag))) ? {}(x0[i]) : x1[i])'.format(fromreal) expr = '({} ? {} : ({} ? {} : {}))'.format( - 'isnan(x0[i])', 'x1[i]', - '(isnan(x1[i].real) || isnan(x1[i].imag))', '{}(x0[i])'.format(fromreal), - default) + 'isnan(x0[i])', 'x1[i]', + '(isnan(x1[i].real) || isnan(x1[i].imag))', '{}(x0[i])'.format(fromreal), + default) else: expr = 'min(x0[i], x1[i])' expr = 'y0[i] = ' + expr return self.binary_op(x0=x1, x1=x2, expr=expr, out=out, - queue=queue, dtype=dtype, name=name, **kwds) + queue=queue, dtype=dtype, name=name, **kwds) + def fabs(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Calculate the absolute value element-wise, outputs real values unless out or dtype is set. @@ -3039,28 +3143,29 @@ class OpenClArrayBackend(ArrayBackend): expr = 'y0[i] = abs(x0[i])' alloc_dtypes = 'f' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype, - alloc_dtypes=alloc_dtypes) + queue=queue, dtype=dtype, + alloc_dtypes=alloc_dtypes) + def absolute(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Calculate the absolute value element-wise, ouputs values as input type unless out or dtype is set. """ if is_complex(x): - expr = 'y0[i] = {}(x0[i])'.format(self.complex_fn('abs',x)) - alloc_dtypes=(complex_to_float_dtype(x),) + expr = 'y0[i] = {}(x0[i])'.format(self.complex_fn('abs', x)) + alloc_dtypes = (complex_to_float_dtype(x),) elif is_fp(x): expr = 'y0[i] = fabs(x0[i])' - alloc_dtypes=None + alloc_dtypes = None else: expr = 'y0[i] = abs(x0[i])' - alloc_dtypes=None + alloc_dtypes = None return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype, alloc_dtypes=alloc_dtypes) + queue=queue, dtype=dtype, alloc_dtypes=alloc_dtypes) def sign(self, x, out=None, - queue=None, dtype=None): + queue=None, dtype=None): """ Returns an element-wise indication of the sign of a number. NaNs values are kept to NaNs to comply with numpy. @@ -3073,18 +3178,20 @@ class OpenClArrayBackend(ArrayBackend): else: expr = 'y0[i] = (sign(x0[i]))' return self.unary_op(x0=x, expr=expr, out=out, - queue=queue, dtype=dtype, - convert_inputs='f') + queue=queue, dtype=dtype, + convert_inputs='f') # RANDOM SAMPLING # ################### -## See https://docs.scipy.org/doc/numpy/reference/routines.random.html +# See https://docs.scipy.org/doc/numpy/reference/routines.random.html # Simple random data + + def rand(self, shape=None, queue=None, - order=default_order, dtype=HYSOP_REAL, - out=None, a=0.0, b=1.0, generator=None): + order=default_order, dtype=HYSOP_REAL, + out=None, a=0.0, b=1.0, generator=None): """ Return samples from the uniform distribution [a,b]. Default generator is clRandom.PhiloxGenerator. @@ -3099,9 +3206,9 @@ class OpenClArrayBackend(ArrayBackend): return out def randn(self, shape=None, queue=None, - order=default_order, dtype=HYSOP_REAL, - out=None, mu=0.0, sigma=1.0, generator=None, - *args): + order=default_order, dtype=HYSOP_REAL, + out=None, mu=0.0, sigma=1.0, generator=None, + *args): """ Return samples from the standard normal distribution [mu,sigma]. Default generator is clRandom.PhiloxGenerator. @@ -3118,53 +3225,55 @@ class OpenClArrayBackend(ArrayBackend): # STATISTICS # ############## -## See https://docs.scipy.org/doc/numpy/reference/routines.sort.html +# See https://docs.scipy.org/doc/numpy/reference/routines.sort.html + +# Order statistics + -#Order statistics def amin(self, a, axis=None, out=None, queue=None, name='amin', **kwds): """ Return the minimum of an array. """ return self.reduce(kargs=(a,), neutral='x0[0]', reduce_expr='min(a,b)', - axis=axis, out=out, queue=queue, name=name, **kwds) + axis=axis, out=out, queue=queue, name=name, **kwds) def amax(self, a, axis=None, out=None, slice=None, queue=None, name='amax', **kwds): """ Return the maximum of an array. """ return self.reduce(kargs=(a,), neutral='x0[0]', reduce_expr='max(a,b)', - axis=axis, out=out, queue=queue, name=name, **kwds) + axis=axis, out=out, queue=queue, name=name, **kwds) def nanmin(self, a, axis=None, out=None, queue=None, name='nanmin', **kwds): """ Return the minimum of an array. """ - reduce_expr='(isnan(a) ? b : (isnan(b) ? a : min(a,b)))' + reduce_expr = '(isnan(a) ? b : (isnan(b) ? a : min(a,b)))' return self.reduce(kargs=(a,), neutral='x0[0]', reduce_expr=reduce_expr, - axis=axis, out=out, queue=queue, name=name, **kwds) + axis=axis, out=out, queue=queue, name=name, **kwds) def nanmax(self, a, axis=None, out=None, slice=None, queue=None, name='nanmax', **kwds): """ Return the maximum of an array. """ - reduce_expr='(isnan(a) ? b : (isnan(b) ? a : max(a,b)))' + reduce_expr = '(isnan(a) ? b : (isnan(b) ? a : max(a,b)))' return self.reduce(kargs=(a,), neutral='x0[0]', reduce_expr=reduce_expr, - axis=axis, out=out, queue=queue, name=name, **kwds) + axis=axis, out=out, queue=queue, name=name, **kwds) def average(self, a, axis=None, weights=None, returned=False, queue=None): """ Compute the weighted average along the specified axis. """ - self._unsupported_argument('average','returned', returned, False) + self._unsupported_argument('average', 'returned', returned, False) if (weights is None): return self.mean(a=a, axis=axis, queue=queue) else: arguments = '__global {ctype} const* x0, __global {ctype} const* w0' arguments = arguments.format(ctype=a.ctype()) return self.reduce(kargs=(a,), neutral='0', reduce_expr='a+b', - axis=axis, out=out, queue=queue, dtype=dtype, - map_expr='w0[i]*x0[i]', - arguments=arguments) / float(a.size) + axis=axis, out=out, queue=queue, dtype=dtype, + map_expr='w0[i]*x0[i]', + arguments=arguments) / float(a.size) def mean(self, a, axis=None, dtype=None, out=None, queue=None): """ @@ -3189,16 +3298,19 @@ class OpenClArrayBackend(ArrayBackend): Compute the median along the specified axis, while ignoring NaNs. """ self._not_implemented_yet('nanmedian') + def nanmean(self, a, axis=None, dtype=None, out=None): """ Compute the arithmetic mean along the specified axis, ignoring NaNs. """ self._not_implemented_yet('nanmean') + def nanstd(self, a, axis=None, dtype=None, out=None, ddof=0): """ Compute the standard deviation along the specified axis, while ignoring NaNs. """ self._not_implemented_yet('nanstd') + def nanvar(self, a, axis=None, dtype=None, out=None, ddof=0): """ Compute the variance along the specified axis, while ignoring NaNs. @@ -3207,19 +3319,19 @@ class OpenClArrayBackend(ArrayBackend): @staticmethod def build_codegen_arguments(args, name, typegen, ctype, mesh_dim, - known_vars, symbolic_mode, - itype='ulong', - const=False, ptr=True, volatile=False, **kargs): + known_vars, symbolic_mode, + itype='ulong', + const=False, ptr=True, volatile=False, **kargs): from hysop.backend.device.codegen.base.utils import ArgDict from hysop.backend.device.codegen.base.variables import \ - CodegenVariable, CodegenVectorClBuiltin + CodegenVariable, CodegenVectorClBuiltin check_instance(args, ArgDict) # strides are in number of elements, offset in bytes - base = '{}_base'.format(name) + base = '{}_base'.format(name) strides = '{}_strides'.format(name) - offset = '{}_offset'.format(name) + offset = '{}_offset'.format(name) assert base not in args assert offset not in args assert strides not in args @@ -3227,19 +3339,19 @@ class OpenClArrayBackend(ArrayBackend): assert 'nl' not in kargs assert 'add_impl_const' not in kargs assert 'init' not in kargs - assert mesh_dim in [1,2,3,4,8,16] + assert mesh_dim in [1, 2, 3, 4, 8, 16] args[base] = CodegenVariable(name=base, typegen=typegen, - ctype=ctype, ptr=ptr, const=const, volatile=volatile, - add_impl_const=True, nl=False, **kargs) + ctype=ctype, ptr=ptr, const=const, volatile=volatile, + add_impl_const=True, nl=False, **kargs) args[strides] = CodegenVectorClBuiltin(name=strides, - typegen=typegen, btype='uint', dim=mesh_dim, - add_impl_const=True, nl=False) + typegen=typegen, btype='uint', dim=mesh_dim, + add_impl_const=True, nl=False) args[offset] = CodegenVariable(name=offset, - typegen=typegen, ctype=itype, - add_impl_const=True, nl=False) + typegen=typegen, ctype=itype, + add_impl_const=True, nl=False) if (offset in known_vars) and (strides in known_vars): args[base].nl = True @@ -3248,8 +3360,7 @@ class OpenClArrayBackend(ArrayBackend): else: args[offset].nl = True - - char_alias = args[base].full_ctype(ctype='char', cast=True, align=True) + char_alias = args[base].full_ctype(ctype='char', cast=True, align=True) ctype_alias = args[base].full_ctype(cast=True, align=True) if (not symbolic_mode) and (offset in known_vars): @@ -3260,9 +3371,9 @@ class OpenClArrayBackend(ArrayBackend): init = '{} $+ {}'.format(base, offset_str) array = CodegenVariable(name=name, typegen=typegen, - ctype=ctype, ptr=ptr, const=const, volatile=volatile, - add_impl_const=True, nl=False, - init=init, **kargs) + ctype=ctype, ptr=ptr, const=const, volatile=volatile, + add_impl_const=True, nl=False, + init=init, **kargs) strides = args[strides] return array, strides diff --git a/hysop/backend/device/opencl/opencl_printer.py b/hysop/backend/device/opencl/opencl_printer.py index 9add3f22d70458bbf3df8b07c1134b9e5ec67894..17e42a0faaa1c47e084cb1aa60b2e9090957524d 100644 --- a/hysop/backend/device/opencl/opencl_printer.py +++ b/hysop/backend/device/opencl/opencl_printer.py @@ -1,7 +1,5 @@ - - import sympy as sm -from sympy.printing.ccode import C99CodePrinter +from sympy.printing.c import C99CodePrinter from hysop.tools.types import check_instance from hysop.backend.device.opencl.opencl_types import OpenClTypeGen @@ -15,9 +13,9 @@ from hysop.backend.device.opencl.opencl_types import OpenClTypeGen # is_negative is_positive # is_rational is_real known_functions = { - 'Abs': [(lambda x: x.is_integer, 'abs'),'fabs'], - 'min': [(lambda x,y: x.is_integer and y.is_integer, 'min'),'fmin'], - 'max': [(lambda x,y: x.is_integer and y.is_integer, 'max'),'fmax'], + 'Abs': [(lambda x: x.is_integer, 'abs'), 'fabs'], + 'min': [(lambda x, y: x.is_integer and y.is_integer, 'min'), 'fmin'], + 'max': [(lambda x, y: x.is_integer and y.is_integer, 'max'), 'fmax'], 'sqrt': 'sqrt', 'gamma': 'tgamma', @@ -104,11 +102,11 @@ class OpenClPrinter(C99CodePrinter): check_instance(typegen, OpenClTypeGen) check_instance(symbol2vars, dict, keys=sm.Symbol, allow_none=True) - super(OpenClPrinter,self).__init__(settings=settings) + super(OpenClPrinter, self).__init__(settings=settings) self.known_functions = dict(known_functions) - self.reserved_words = set(reserved_words) - self.typegen = typegen + self.reserved_words = set(reserved_words) + self.typegen = typegen self.symbol2vars = symbol2vars def dump_symbol(self, expr): @@ -116,36 +114,44 @@ class OpenClPrinter(C99CodePrinter): if expr in symbol2vars: return self._print(symbol2vars[expr]) else: - return super(OpenClPrinter,self)._print_Symbol(expr) + return super(OpenClPrinter, self)._print_Symbol(expr) + def dump_rational(self, expr): return self.typegen.dump(expr) + def dump_float(self, expr): return self.typegen.dump(expr) - def _print_Symbol(self, expr): return self.dump_symbol(expr) + def _print_Rational(self, expr): return self.dump_rational(expr) + def _print_PythonRational(self, expr): return self.dump_rational(expr) + def _print_Fraction(self, expr): return self.dump_rational(expr) + def _print_mpq(self, expr): return self.dump_rational(expr) + def _print_Float(self, expr): return self.dump_float(expr) # last resort printer (if _print_CLASS is not found) - def emptyPrinter(self,expr): + def emptyPrinter(self, expr): return self.typegen.dump(expr) + def dump_clcode(expr, typegen, **kargs): """Return OpenCL representation of the given expression as a string.""" p = OpenClPrinter(typegen=typegen, **kargs) s = p.doprint(expr) return s + def print_clcode(expr, typegen, **kargs): """Prints OpenCL representation of the given expression.""" - print(dump_clcode(expr,typegen=typegen,**kargs)) + print(dump_clcode(expr, typegen=typegen, **kargs)) diff --git a/hysop/backend/host/host_array_backend.py b/hysop/backend/host/host_array_backend.py index 4da33873efb5a94305d30d7fd63de905559aca40..3818b43427b08cc5846a0cdc8641bab328f1faba 100644 --- a/hysop/backend/host/host_array_backend.py +++ b/hysop/backend/host/host_array_backend.py @@ -6,7 +6,7 @@ from hysop.constants import HYSOP_REAL, HYSOP_INTEGER, HYSOP_BOOL from hysop.tools.decorators import wraps from hysop.tools.numerics import is_complex from hysop.tools.types import check_instance, to_tuple, first_not_None -from hysop.tools.misc import get_default_args, args2kargs, kargs2args, get_argnames +from hysop.tools.misc import get_default_args, args2kargs, kargs2args, get_argnames from hysop.tools.hash import hash_id from hysop.tools.warning import HysopWarning from hysop.core.arrays import default_order, MemoryOrdering, MemoryType @@ -14,6 +14,7 @@ from hysop.core.arrays.array_backend import ArrayBackend from hysop.core.memory.allocator import AllocatorBase from hysop.backend.host.host_buffer import HostBuffer + def numpy_method(f): @wraps(f) def wrapper(*args, **kwargs): @@ -22,28 +23,28 @@ def numpy_method(f): # build full args dictionnary kargs = get_default_args(f) kargs.update(kwargs) - kargs.update(args2kargs(f,args)) + kargs.update(args2kargs(f, args)) argnames, varargs = get_argnames(f) - has_args = ('args' in varargs) if varargs else False + has_args = ('args' in varargs) if varargs else False has_kwargs = ('kwargs' in varargs) if varargs else False if has_args or has_kwargs: - msg='Wrapper does not support varargs and kwargs signatures for function {}:{}.' - msg=msg.format(args[0].__name__,f.__name__) + msg = 'Wrapper does not support varargs and kwargs signatures for function {}:{}.' + msg = msg.format(args[0].__name__, f.__name__) raise RuntimeError(msg) for k in kargs.keys(): if k not in argnames: - msg='Unknown argument {} in function {}::{}(), possible ones are {}.' - msg=msg.format(k, getattr(args[0], '__name__', type(args[0]).__name__), f.__name__, argnames) + msg = 'Unknown argument {} in function {}::{}(), possible ones are {}.' + msg = msg.format(k, getattr(args[0], '__name__', type(args[0]).__name__), f.__name__, argnames) raise ValueError(msg) # format input arguments for numpy backend = kargs.pop('self') assert isinstance(backend, HostArrayBackend) - _,kargs = backend._prepare_args(**kargs) + _, kargs = backend._prepare_args(**kargs) backend._alloc_outputs(fname=f.__name__, kargs=kargs) @@ -84,7 +85,7 @@ class HostArrayBackend(ArrayBackend): def __init__(self, allocator, **kwds): check_instance(allocator, AllocatorBase) assert allocator.is_on_host(), 'allocator does not allocate buffers on host.' - super(HostArrayBackend,self).__init__(allocator=allocator, **kwds) + super(HostArrayBackend, self).__init__(allocator=allocator, **kwds) def __str__(self): return 'host array backend' @@ -95,16 +96,15 @@ class HostArrayBackend(ArrayBackend): def get_host_array_backend(self): return self - host_array_backend=property(get_host_array_backend) + host_array_backend = property(get_host_array_backend) def short_description(self): return ':HostBackend: tag={}, allocator={}'.format( - self.tag, self.allocator.full_tag) + self.tag, self.allocator.full_tag) ############################ # BACKEND SPECIFIC METHODS # - def can_wrap(self, handle): """ Should return True if handle is an Array or a array handle corresponding @@ -125,13 +125,13 @@ class HostArrayBackend(ArrayBackend): check_instance(handle, np.ndarray) # if handle.dtype==np.bool: - # import warnings - # msg='HostArrayBackend: numpy np.bool array converted to HYSOP_BOOL={}.' - # msg=msg.format(HYSOP_BOOL.__name__) - # warnings.warn(msg, HysopWarning) - # return HostArray(backend=self, handle=handle.astype(HYSOP_BOOL)) + # import warnings + # msg='HostArrayBackend: numpy np.bool array converted to HYSOP_BOOL={}.' + # msg=msg.format(HYSOP_BOOL.__name__) + # warnings.warn(msg, HysopWarning) + # return HostArray(backend=self, handle=handle.astype(HYSOP_BOOL)) # else: - # return HostArray(backend=self, handle=handle) + # return HostArray(backend=self, handle=handle) return HostArray(backend=self, handle=handle) def copyto(self, dst, src, reshape=False, queue=None, synchronize=True, **kwds): @@ -160,7 +160,7 @@ class HostArrayBackend(ArrayBackend): elif isinstance(dst, OpenClArray): queue = first_not_None(queue, dst.default_queue) from hysop.backend.device.opencl.opencl_copy_kernel_launchers \ - import OpenClCopyBufferRectLauncher + import OpenClCopyBufferRectLauncher kl = OpenClCopyBufferRectLauncher.from_slices('copyto', src=src, dst=dst) evt = kl(queue=queue) if synchronize: @@ -175,13 +175,14 @@ class HostArrayBackend(ArrayBackend): ########################### # ARRAY CREATION ROUTINES # -## See https://docs.scipy.org/doc/numpy/reference/routines.array-creation.html +# See https://docs.scipy.org/doc/numpy/reference/routines.array-creation.html ## ALLOCATED WITH BACKEND ALLOCATOR ## + def array(self, shape, dtype=HYSOP_REAL, order=default_order, - min_alignment=None, buf=None, offset=0): + min_alignment=None, buf=None, offset=0): """ Create a HostArray, see np.ndarray constructor. If buf is None, a new one is allocated from backend allocator. @@ -190,18 +191,18 @@ class HostArrayBackend(ArrayBackend): order = self._arg(order) shape = to_tuple(shape) - if dtype==np.bool: - dtype=HYSOP_BOOL + if dtype == np.bool_: + dtype = HYSOP_BOOL import warning - msg='HostArrayBackend: numpy bool array converted to hysop_bool={}.'.format(dtype) + msg = 'HostArrayBackend: numpy bool array converted to hysop_bool={}.'.format(dtype) warnings.warn(msg, HysopWarning) if (buf is None): - assert offset==0 + assert offset == 0 allocator = self.allocator dtype = np.dtype(dtype) - (size,nbytes,alignment) = self.get_alignment_and_size(shape=shape, - dtype=dtype, min_alignment=min_alignment) + (size, nbytes, alignment) = self.get_alignment_and_size(shape=shape, + dtype=dtype, min_alignment=min_alignment) alloc = allocator.allocate_aligned(size, alignment=alignment) handle = HostBuffer(buffer=alloc, shape=shape, dtype=dtype, order=order, offset=0, strides=None, size=size) @@ -213,46 +214,43 @@ class HostArrayBackend(ArrayBackend): return array def empty(self, shape, dtype=HYSOP_REAL, - order=default_order, min_alignment=None): + order=default_order, min_alignment=None): """ Return a new array of given shape and type, without initializing entries. Data is allocated from backend allocator. """ a = self.array(shape=shape, dtype=dtype, - order=order, min_alignment=min_alignment) + order=order, min_alignment=min_alignment) return a - def full(self, shape, fill_value, dtype=HYSOP_REAL, - order=default_order, min_alignment=None): + order=default_order, min_alignment=None): """ Return a new array of given shape and type, filled with fill_value. Data is allocated from backend allocator. """ a = self.empty(shape=shape, dtype=dtype, - order=order, min_alignment=min_alignment) + order=order, min_alignment=min_alignment) self.fill(a=a, value=fill_value) return a def zeros(self, shape, dtype=HYSOP_REAL, - order=default_order, min_alignment=None): + order=default_order, min_alignment=None): """ Return a new array of given shape and type, filled with zeros. Data is allocated from backend allocator. """ return self.full(shape=shape, dtype=dtype, order=order, - fill_value=0, min_alignment=min_alignment) + fill_value=0, min_alignment=min_alignment) def ones(self, shape, dtype=HYSOP_REAL, - order=default_order, min_alignment=None): + order=default_order, min_alignment=None): """ Return a new array of given shape and type, filled with ones. Data is allocated from backend allocator. """ return self.full(shape=shape, dtype=dtype, order=order, - fill_value=1, min_alignment=min_alignment) - - + fill_value=1, min_alignment=min_alignment) def empty_like(self, a, dtype=None, order=None, subok=True, shape=None): """ @@ -271,9 +269,9 @@ class HostArrayBackend(ArrayBackend): except AttributeError: order = default_order return self.empty( - shape = first_not_None(shape, a.shape), - dtype = first_not_None(dtype, a.dtype), - order = order) + shape=first_not_None(shape, a.shape), + dtype=first_not_None(dtype, a.dtype), + order=order) def full_like(self, a, fill_value, dtype=None, order=None, subok=True, shape=None): """ @@ -289,14 +287,14 @@ class HostArrayBackend(ArrayBackend): Return an array of zeros with the same shape and type as a given array. Data is allocated from backend allocator. """ - return self.full_like(a=a,fill_value=0,dtype=dtype,order=order,subok=subok,shape=shape) + return self.full_like(a=a, fill_value=0, dtype=dtype, order=order, subok=subok, shape=shape) def ones_like(self, a, dtype=None, order=None, subok=True, shape=None): """ Return an array of ones with the same shape and type as a given array. Data is allocated from backend allocator. """ - return self.full_like(a=a,fill_value=1,dtype=dtype,order=order,subok=subok,shape=shape) + return self.full_like(a=a, fill_value=1, dtype=dtype, order=order, subok=subok, shape=shape) # Filling facility def fill(self, a, value): @@ -315,6 +313,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def identity(self, n, dtype=None): """ @@ -322,6 +321,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + def copy(self, a, order=MemoryOrdering.SAME_ORDER): """ Return an array copy of the given object. @@ -337,6 +337,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def asanyarray(self, a, dtype=None, order=default_order): """ @@ -344,6 +345,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def asmatrix(self, data, dtype=None): """ @@ -351,6 +353,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def frombuffer(self, afer, dtype=HYSOP_REAL, count=-1, offset=0): """ @@ -358,6 +361,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def fromfile(self, file, dtype=HYSOP_REAL, count=-1, sep=''): """ @@ -365,6 +369,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def fromfunction(self, function, shape, dtype=HYSOP_REAL): """ @@ -372,6 +377,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def fromiter(self, iterable, dtype=HYSOP_REAL, count=-1): """ @@ -379,6 +385,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def fromstring(self, string, dtype=HYSOP_REAL, count=-1, sep=''): """ @@ -386,16 +393,17 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def loadtxt(self, fname, dtype=HYSOP_REAL, comments='#', delimiter=None, - converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0): + converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0): """ Load data from a text file. Data is *not* allocated from backend allocator. """ pass -#Numerical ranges +# Numerical ranges def arange(self, *args, **kargs): """ @@ -404,7 +412,7 @@ class HostArrayBackend(ArrayBackend): """ if 'dtype' not in kargs: kargs['dtype'] = HYSOP_INTEGER - handle = np.arange(*args,**kargs) + handle = np.arange(*args, **kargs) return self.wrap(handle) @numpy_method @@ -414,6 +422,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def logspace(self, start, stop, num=50, endpoint=True, base=10.0, dtype=HYSOP_REAL): """ @@ -421,6 +430,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def geomspace(self, start, stop, num=50, endpoint=True, dtype=HYSOP_REAL): """ @@ -428,6 +438,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def meshgrid(self, *xi, **kwargs): """ @@ -436,7 +447,7 @@ class HostArrayBackend(ArrayBackend): """ pass -#Building matrices +# Building matrices @numpy_method def diag(self, v, k=0): """ @@ -444,6 +455,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def diagflat(self, v, k=0): """ @@ -451,6 +463,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def tri(self, N, M=None, k=0, dtype=HYSOP_REAL): """ @@ -458,6 +471,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def tril(self, m, k): """ @@ -465,6 +479,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def triu(self, m, k=0): """ @@ -472,6 +487,7 @@ class HostArrayBackend(ArrayBackend): Data is *not* allocated from backend allocator. """ pass + @numpy_method def vander(self, x, N=None, increasing=False): """ @@ -483,15 +499,16 @@ class HostArrayBackend(ArrayBackend): ############################### # ARRAY MANIPULATION ROUTINES # -## See https://docs.scipy.org/doc/numpy/reference/routines.array-manipulation.html +# See https://docs.scipy.org/doc/numpy/reference/routines.array-manipulation.html -#Changing array shape +# Changing array shape @numpy_method def reshape(self, a, newshape, order=default_order): """ Gives a new shape to an array without changing its data. """ pass + @numpy_method def ravel(self, a, order=MemoryOrdering.SAME_ORDER): """ @@ -499,26 +516,29 @@ class HostArrayBackend(ArrayBackend): """ pass -#Transpose-like operations -## /!\ those functions can alter the transposition state /!\ +# Transpose-like operations +# /!\ those functions can alter the transposition state /!\ @numpy_method def moveaxis(self, a, source, destination): """ Move axes of an array to new positions. """ pass + @numpy_method def rollaxis(self, a, axis, start=0): """ Roll the specified axis backwards, until it lies in a given position. """ pass + @numpy_method def swapaxes(self, a, axis1, axis2): """ Interchange two axes of an array. """ pass + @numpy_method def transpose(self, a, axes=None): """ @@ -527,43 +547,49 @@ class HostArrayBackend(ArrayBackend): pass -#Changing number of dimensions +# Changing number of dimensions @numpy_method def atleast_1d(self, *arys): """ Convert inputs to arrays with at least one dimension. """ pass + @numpy_method def atleast_2d(self, *arys): """ View inputs as arrays with at least two dimensions. """ pass + @numpy_method def atleast_3d(self, *arys): """ View inputs as arrays with at least three dimensions. """ pass + @numpy_method def broadcast_to(self, array, shape, subok=False): """ Broadcast an array to a new shape. """ pass + @numpy_method def broadcast_arrays(self, *args, **kwargs): """ Broadcast any number of arrays against each other. """ pass + @numpy_method def expand_dims(self, a, axis): """ Expand the shape of an array. """ pass + @numpy_method def squeeze(self, a, axis=None): """ @@ -571,31 +597,35 @@ class HostArrayBackend(ArrayBackend): """ pass -#Changing kind of array +# Changing kind of array @numpy_method def asfortranarray(self, a, dtype=None): """ Return an array laid out in Fortran order in memory. """ pass + @numpy_method def ascontiguousarray(self, a, dtype=None): """ Return a contiguous array in memory (C order). """ pass + @numpy_method def asarray_chkfinite(self, a, dtype=None, order=default_order): """ Convert the input to an array, checking for NaNs or Infs. """ pass + @numpy_method def asscalar(self, a): """ Convert an array of size 1 to its scalar equivalent. """ pass + @numpy_method def require(self, a, dtype=None, requirements=None): """ @@ -603,37 +633,42 @@ class HostArrayBackend(ArrayBackend): """ pass -#Joining arrays +# Joining arrays @numpy_method def concatenate(self, a, axis=0): """ Join a sequence of arrays along an existing axis. """ pass + @numpy_method def stack(self, arrays, axis=0): """ Join a sequence of arrays along a new axis. """ pass + @numpy_method def column_stack(self, tup): """ Stack 1-D arrays as columns into a 2-D array. """ pass + @numpy_method def dstack(self, tup): """ Stack arrays in sequence depth wise (along third axis). """ pass + @numpy_method def hstack(self, tup): """ Stack arrays in sequence horizontally (column wise). """ pass + @numpy_method def vstack(self, tup): """ @@ -641,31 +676,35 @@ class HostArrayBackend(ArrayBackend): """ pass -#Splitting arrays +# Splitting arrays @numpy_method def split(self, ary, indices_or_sections, axis=0): """ Split an array into multiple sub-arrays. """ pass + @numpy_method def array_split(self, ary, indices_or_sections, axis=0): """ Split an array into multiple sub-arrays. """ pass + @numpy_method def dsplit(self, ary, indices_or_sections): """ Split array into multiple sub-arrays along the 3rd axis (depth). """ pass + @numpy_method def hsplit(self, ary, indices_or_sections): """ Split an array into multiple sub-arrays horizontally (column-wise). """ pass + @numpy_method def vsplit(self, ary, indices_or_sections): """ @@ -673,13 +712,14 @@ class HostArrayBackend(ArrayBackend): """ pass -#Tiling arrays +# Tiling arrays @numpy_method def tile(self, A, reps): """ Construct an array by repeating A the number of times given by reps. """ pass + @numpy_method def repeat(self, a, repeats, axis=None): """ @@ -687,37 +727,42 @@ class HostArrayBackend(ArrayBackend): """ pass -#Adding and removing elements +# Adding and removing elements @numpy_method def delete(self, arr, obj, axis=None): """ Return a new array with sub-arrays along an axis deleted. """ pass + @numpy_method def insert(self, arr, obj, values, axis=None): """ Insert values along the given axis before the given indices. """ pass + @numpy_method def append(self, arr, values, axis=None): """ Append values to the end of an array. """ pass + @numpy_method def resize(self, a, new_shape): """ Return a new array with the specified shape. """ pass + @numpy_method def trim_zeros(self, filt, trim='fb'): """ Trim the leading and/or trailing zeros from a 1-D array or sequence. """ pass + @numpy_method def unique(self, ar, return_index=False, return_inverse=False, return_counts=False): """ @@ -725,33 +770,37 @@ class HostArrayBackend(ArrayBackend): """ pass -#Rearranging elements +# Rearranging elements @numpy_method def flip(self, m, axis): """ Reverse the order of elements in an array along the given axis. """ pass + @numpy_method def fliplr(self, m): """ Flip array in the left/right direction. """ pass + @numpy_method def flipud(self, m): """ Flip array in the up/down direction. """ pass + @numpy_method def roll(self, a, shift, axis=None): """ Roll array elements along a given axis. """ pass + @numpy_method - def rot90(self, m, k=1, axes=(0,1)): + def rot90(self, m, k=1, axes=(0, 1)): """ Rotate an array by 90 degrees in the plane specified by axes. """ @@ -760,7 +809,7 @@ class HostArrayBackend(ArrayBackend): ##################### # BINARY OPERATIONS # -## See https://docs.scipy.org/doc/numpy/reference/routines.bitwise.html +# See https://docs.scipy.org/doc/numpy/reference/routines.bitwise.html # Elementwise bit operations @numpy_method @@ -769,12 +818,14 @@ class HostArrayBackend(ArrayBackend): Compute the bit-wise AND of two arrays element-wise. """ pass + @numpy_method def bitwise_or(self, x1, x2, out=None): """ Compute the bit-wise OR of two arrays element-wise. """ pass + @numpy_method def bitwise_xor(self, x1, x2, out=None): """ @@ -788,12 +839,14 @@ class HostArrayBackend(ArrayBackend): Compute bit-wise inversion, or bit-wise NOT, element-wise. """ pass + @numpy_method def left_shift(self, x1, x2, out=None): """ Shift the bits of an integer to the left. """ pass + @numpy_method def right_shift(self, x1, x2, out=None): """ @@ -801,13 +854,14 @@ class HostArrayBackend(ArrayBackend): """ pass -#Bit packing +# Bit packing @numpy_method def packbits(self, myarray, axis=None): """ Packs the elements of a binary-valued array into bits in a uint8 array. """ pass + @numpy_method def unpackbits(self, myarray, axis=None): """ @@ -815,7 +869,7 @@ class HostArrayBackend(ArrayBackend): """ pass -#Output formatting +# Output formatting @numpy_method def binary_repr(self, num, width=None): """ @@ -826,39 +880,44 @@ class HostArrayBackend(ArrayBackend): ############################## # DISCRETE FOURIER TRANSFORM # -## See https://docs.scipy.org/doc/numpy/reference/routines.fft.html +# See https://docs.scipy.org/doc/numpy/reference/routines.fft.html -#Standard FFTs +# Standard FFTs @numpy_method def fft(self, a, n=None, axis=-1, norm=None): """ Compute the one-dimensional discrete Fourier Transform. """ pass + @numpy_method def ifft(self, a, n=None, axis=-1, norm=None): """ Compute the one-dimensional inverse discrete Fourier Transform. """ pass + @numpy_method def fft2(self, a, s=None, axes=None, norm=None): """ Compute the 2-dimensional discrete Fourier Transform """ pass + @numpy_method def ifft2(self, a, s=None, axes=None, norm=None): """ Compute the 2-dimensional inverse discrete Fourier Transform. """ pass + @numpy_method def fftn(self, a, s=None, axes=None, norm=None): """ Compute the N-dimensional discrete Fourier Transform. """ pass + @numpy_method def ifftn(self, a, s=None, axes=None, norm=None): """ @@ -866,37 +925,42 @@ class HostArrayBackend(ArrayBackend): """ pass -#Real FFTs +# Real FFTs @numpy_method def rfft(self, a, n=None, axis=-1, norm=None): """ Compute the one-dimensional discrete Fourier Transform for real input. """ pass + @numpy_method def irfft(self, a, n=None, axis=-1, norm=None): """ Compute the inverse of the n-point DFT for real input. """ pass + @numpy_method - def rfft2(self, a, s=None, axes=(-2,-1), norm=None): + def rfft2(self, a, s=None, axes=(-2, -1), norm=None): """ Compute the 2-dimensional FFT of a real array. """ pass + @numpy_method - def irfft2(self, a, s=None, axes=(-2,-1), norm=None): + def irfft2(self, a, s=None, axes=(-2, -1), norm=None): """ Compute the 2-dimensional inverse FFT of a real array. """ pass + @numpy_method def rfftn(self, a, s=None, axes=None, norm=None): """ Compute the N-dimensional discrete Fourier Transform for real input. """ pass + @numpy_method def irfftn(self, a, s=None, axes=None, norm=None): """ @@ -904,13 +968,14 @@ class HostArrayBackend(ArrayBackend): """ pass -#Hermitian FFTs +# Hermitian FFTs @numpy_method def hfft(self, a, n=None, axis=-1, norm=None): """ Compute the FFT of a signal that has Hermitian symmetry, i.e., a real spectrum. """ pass + @numpy_method def ihfft(self, a, n=None, axis=-1, norm=None): """ @@ -918,25 +983,28 @@ class HostArrayBackend(ArrayBackend): """ pass -#Helper routines +# Helper routines @numpy_method def fftfreq(self, n=None, d=1.0): """ Return the Discrete Fourier Transform sample frequencies. """ pass + @numpy_method def rfftfreq(self, n=None, d=1.0): """ Return the Discrete Fourier Transform sample frequencies (for usage with rfft, irfft). """ pass + @numpy_method def fftshift(self, x, axes=None): """ Shift the zero-frequency component to the center of the spectrum. """ pass + @numpy_method def ifftshift(self, x, axes=None): """ @@ -947,7 +1015,7 @@ class HostArrayBackend(ArrayBackend): ########################## # FUNCTIONAL PROGRAMMING # -## See https://docs.scipy.org/doc/numpy/reference/routines.functional.html +# See https://docs.scipy.org/doc/numpy/reference/routines.functional.html @numpy_method def vectorize(self, pyfunc, otypes=None, doc=None, excluded=None, cache=False, signature=None): @@ -955,12 +1023,14 @@ class HostArrayBackend(ArrayBackend): Generalized function class. """ pass + @numpy_method def frompyfunc(self, func, nin, nout): """ Takes an arbitrary Python function and returns a NumPy ufunc. """ pass + @numpy_method def piecewise(self, x, condlist, funclist, *args, **kw): """ @@ -970,7 +1040,7 @@ class HostArrayBackend(ArrayBackend): #################### # INPUT AND OUTPUT # -## See https://docs.scipy.org/doc/numpy/reference/routines.io.html +# See https://docs.scipy.org/doc/numpy/reference/routines.io.html # NumPy binary files (NPY, NPZ) @numpy_method @@ -979,18 +1049,21 @@ class HostArrayBackend(ArrayBackend): Load arrays or pickled objects from .npy, .npz or pickled files. """ pass + @numpy_method def save(self, arr, file, allow_pickle=True, fix_imports=True): """ Save an array to a binary file in NumPy .npy format. """ pass + @numpy_method def savez(self, file, *args, **kwds): """ Save several arrays into a single file in uncompressed .npz format. """ pass + @numpy_method def savez_compressed(self, file, *args, **kwds): """ @@ -1001,34 +1074,38 @@ class HostArrayBackend(ArrayBackend): # Text files @numpy_method def loadtxt(self, dtype=HYSOP_REAL, comments='#', delimiter=None, - converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0): + converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0): """ Load data from a text file. """ pass + @numpy_method def savetxt(self, fname, X, fmt='%.18e', delimiter=' ', newline='\n', - header='', footer='', comments='# '): + header='', footer='', comments='# '): """ Save an array to a text file. """ pass + @numpy_method def genfromtxt(self, fname, dtype=HYSOP_REAL, comments='#', delimiter=None, - skip_header=0, skip_footer=0, converters=None, missing_values=None, - filling_values=None, usecols=None, names=None, excludelist=None, deletechars=None, - replace_space='_', autostrip=False, case_sensitive=True, defaultfmt='f%i', - unpack=None, usemask=False, loose=True, invalid_raise=True, max_rows=None): + skip_header=0, skip_footer=0, converters=None, missing_values=None, + filling_values=None, usecols=None, names=None, excludelist=None, deletechars=None, + replace_space='_', autostrip=False, case_sensitive=True, defaultfmt='f%i', + unpack=None, usemask=False, loose=True, invalid_raise=True, max_rows=None): """ Load data from a text file, with missing values handled as specified. """ pass + @numpy_method def fromregex(self, file, regexp, dtype): """ Construct an array from a text file, using regular expression parsing. """ pass + @numpy_method def fromstring(self, string, dtype=HYSOP_REAL, count=-1, sep=''): """ @@ -1039,51 +1116,56 @@ class HostArrayBackend(ArrayBackend): # String formatting @numpy_method def array2string(self, a, max_line_width=None, precision=None, suppress_small=None, - separator=' ', prefix='', style=repr, formatter=None): + separator=' ', prefix='', style=repr, formatter=None): """ Return a string representation of an array. """ pass + @numpy_method def array_repr(self, arr, max_line_width=None, precision=None, supress_small=None): """ Return the string representation of an array. """ pass + @numpy_method def array_str(self, a, max_line_width=None, precision=None, suppress_small=None): """ Return a string representation of the data in an array. """ pass -#Text formatting options +# Text formatting options @numpy_method def set_printoptions(self, precision=None, threshold=None, edgeitems=None, - linewidth=None, suppress=None, nanstr=None, - infstr=None, formatter=None): + linewidth=None, suppress=None, nanstr=None, + infstr=None, formatter=None): """ Set printing options. """ pass + @numpy_method def get_printoptions(self): """ Return the current print options. """ pass + @numpy_method def set_string_function(self, f, repr=True): """ Set a Python function to be used when pretty printing arrays. """ pass -#Base-n representations +# Base-n representations @numpy_method def binary_repr(self, num, width=None): """ Return the binary representation of the input number as a string. """ pass + @numpy_method def base_repr(self, number, base=2, padding=0): """ @@ -1094,58 +1176,66 @@ class HostArrayBackend(ArrayBackend): ###################### ### LINEAR ALGEBRA ### -## See https://docs.scipy.org/doc/numpy/reference/routines.linalg.html +# See https://docs.scipy.org/doc/numpy/reference/routines.linalg.html -#Matrix and vector products +# Matrix and vector products @numpy_method def dot(self, a, b, out=None): """ Dot product of two arrays. """ pass + @numpy_method def vdot(self, a, b): """ Return the dot product of two vectors. """ pass + @numpy_method def inner(self, a, b): """ Inner product of two arrays. """ pass + @numpy_method def outer(self, a, b, out=None): """ Compute the outer product of two vectors. """ pass + @numpy_method def matmul(self, a, b, out=None): """ Matrix product of two arrays. """ pass + @numpy_method def tensordot(self, a, b, axes=2): """ Compute tensor dot product along specified axes for arrays >= 1-D. """ pass + @numpy_method def einsum(self, subscripts, out=None, dtype=None, order=MemoryOrdering.SAME_ORDER, - casting='safe', optimize=False, *operands): + casting='safe', optimize=False, *operands): """ Evaluates the Einstein summation convention on the operands. """ pass + @numpy_method def matrix_power(self, M, n): """ Raise a square matrix to the integer power n. """ pass + @numpy_method def kron(self, a, b): """ @@ -1153,19 +1243,21 @@ class HostArrayBackend(ArrayBackend): """ pass -#Decompositions +# Decompositions @numpy_method def cholesky(self, a): """ Cholesky decomposition. """ pass + @numpy_method def qr(self, a, mode='reduced'): """ Compute the qr factorization of a matrix. """ pass + @numpy_method def svd(self, a, full_matrices=True, compute_uv=True): """ @@ -1173,25 +1265,28 @@ class HostArrayBackend(ArrayBackend): """ pass -#Matrix eigenvalues +# Matrix eigenvalues @numpy_method def eig(self, a): """ Compute the eigenvalues and right eigenvectors of a square array. """ pass + @numpy_method def eigh(self, a, UPLO='L'): """ Return the eigenvalues and eigenvectors of a Hermitian or symmetric matrix. """ pass + @numpy_method def eigvals(self, a): """ Compute the eigenvalues of a general matrix. """ pass + @numpy_method def eigvalsh(self, a, UPLO='L'): """ @@ -1199,37 +1294,42 @@ class HostArrayBackend(ArrayBackend): """ pass -#Norms and other numbers +# Norms and other numbers @numpy_method def norm(self, x, ord=None, axis=None, keepdims=False): """ Matrix or vector norm. """ pass + @numpy_method def cond(self, x, p=None): """ Compute the condition number of a matrix. """ pass + @numpy_method def det(self, a): """ Compute the determinant of an array. """ pass + @numpy_method def matrix_rank(self, M, tol=None): """ Return matrix rank of array using SVD method """ pass + @numpy_method def slogdet(self, a): """ Compute the sign and natural logarithm of the determinant of an array. """ pass + @numpy_method def trace(self, a, offset=0, axis1=0, axis2=1, dtype=None, out=None): """ @@ -1237,37 +1337,42 @@ class HostArrayBackend(ArrayBackend): """ pass -#Solving equations and inverting matrices +# Solving equations and inverting matrices @numpy_method def solve(self, a, b): """ Solve a linear matrix equation, or system of linear scalar equations. """ pass + @numpy_method def tensorsolve(self, a, b, axes=None): """ Solve the tensor equation a x = b for x. """ pass + @numpy_method def lstsq(self, a, b, rcond=-1): """ Return the least-squares solution to a linear matrix equation. """ pass + @numpy_method def inv(self, a): """ Compute the (multiplicative) inverse of a matrix. """ pass + @numpy_method def pinv(self, a, rcond=1e-15): """ Compute the (Moore-Penrose) pseudo-inverse of a matrix. """ pass + @numpy_method def tensorinv(self, a, ind=2): """ @@ -1278,15 +1383,16 @@ class HostArrayBackend(ArrayBackend): ################### # LOGIC FUNCTIONS # -## See https://docs.scipy.org/doc/numpy/reference/routines.logic.html +# See https://docs.scipy.org/doc/numpy/reference/routines.logic.html -#Truth value testing +# Truth value testing @numpy_method def any(self, a, axis=None, out=None): """ Test whether any array elements along a given axis evaluate to True. """ pass + @numpy_method def all(self, a, axis=None, out=None): """ @@ -1294,31 +1400,35 @@ class HostArrayBackend(ArrayBackend): """ pass -#Array contents +# Array contents @numpy_method def isfinite(self, x, out=None): """ Test element-wise for finiteness (not infinity or not Not a Number). """ pass + @numpy_method def isinf(self, x, out=None): """ Test element-wise for positive or negative infinity. """ pass + @numpy_method def isnan(self, x, out=None): """ Test element-wise for NaN and return result as a boolean array. """ pass + @numpy_method def isneginf(self, x, out=None): """ Test element-wise for negative infinity, return result as bool array. """ pass + @numpy_method def isposinf(self, x, out=None): """ @@ -1326,25 +1436,28 @@ class HostArrayBackend(ArrayBackend): """ pass -#Logical operations +# Logical operations @numpy_method def logical_and(self, x1, x2, out=None): """ Compute the truth value of x1 AND x2 element-wise. """ pass + @numpy_method def logical_or(self, x1, x2, out=None): """ Compute the truth value of x1 OR x2 element-wise. """ pass + @numpy_method def logical_not(self, x, out=None): """ Compute the truth value of NOT x element-wise. """ pass + @numpy_method def logical_xor(self, x1, x2, out=None): """ @@ -1352,61 +1465,70 @@ class HostArrayBackend(ArrayBackend): """ pass -#Comparisson +# Comparisson @numpy_method def allclose(self, a, b, rtol=1e-05, atol=1e-08, equal_nan=False): """ Returns True if two arrays are element-wise equal within a tolerance. """ pass + @numpy_method def isclose(self, a, b, rtol=1e-05, atol=1e-08, equal_nan=False): """ Returns a boolean array where two arrays are element-wise equal within a tolerance. """ pass + @numpy_method def array_equal(self, a1, a2): """ True if two arrays have the same shape and elements, False otherwise. """ pass + @numpy_method def array_equiv(self, a1, a2): """ returns True if input arrays are shape consistent and all elements equal. """ pass + @numpy_method def greater(self, x1, x2, out=None): """ Return the truth value of (x1 > x2) element-wise. """ pass + @numpy_method def greater_equal(self, x1, x2, out=None): """ Return the truth value of (x1 >= x2) element-wise. """ pass + @numpy_method def less(self, x1, x2, out=None): """ Return the truth value of (x1 < x2) element-wise. """ pass + @numpy_method def less_equal(self, x1, x2, out=None): """ Return the truth value of (x1 =< x2) element-wise. """ pass + @numpy_method def equal(self, x1, x2, out=None): """ Return (x1 == x2) element-wise. """ pass + @numpy_method def not_equal(self, x1, x2, out=None): """ @@ -1417,7 +1539,7 @@ class HostArrayBackend(ArrayBackend): ########################## # MATHEMATICAL FUNCTIONS # -## See https://docs.scipy.org/doc/numpy/reference/routines.math.html +# See https://docs.scipy.org/doc/numpy/reference/routines.math.html # Trigonometric functions @numpy_method @@ -1426,12 +1548,14 @@ class HostArrayBackend(ArrayBackend): Trigonometric sine, element-wise. """ pass + @numpy_method def cos(self, x, out=None): """ Cosine element-wise. """ pass + @numpy_method def tan(self, x, out=None): """ @@ -1445,18 +1569,21 @@ class HostArrayBackend(ArrayBackend): Inverse sine, element-wise. """ pass + @numpy_method def arccos(self, x, out=None): """ Trigonometric inverse cosine, element-wise. """ pass + @numpy_method def arctan(self, x, out=None): """ Trigonometric inverse tangent, element-wise. """ pass + @numpy_method def arctan2(self, x1, x2, out=None): """ @@ -1470,18 +1597,21 @@ class HostArrayBackend(ArrayBackend): Given the legs of a right triangle, return its hypotenuse. """ pass + @numpy_method def unwrap(self, p, discont=3.141592653589793, axis=-1): """ Unwrap by changing deltas between values to 2*pi complement. """ pass + @numpy_method def deg2rad(self, x, out=None): """ Convert angles from degrees to radians. """ pass + @numpy_method def rad2deg(self, x, out=None): """ @@ -1496,12 +1626,14 @@ class HostArrayBackend(ArrayBackend): Hyperbolic sine, element-wise. """ pass + @numpy_method def cosh(self, x, out=None): """ Hyperbolic cosine, element-wise. """ pass + @numpy_method def tanh(self, x, out=None): """ @@ -1515,12 +1647,14 @@ class HostArrayBackend(ArrayBackend): Inverse hyperbolic sine element-wise. """ pass + @numpy_method def arccosh(self, x, out=None): """ Inverse hyperbolic cosine, element-wise. """ pass + @numpy_method def arctanh(self, x, out=None): """ @@ -1549,18 +1683,21 @@ class HostArrayBackend(ArrayBackend): Round elements of the array to the nearest integer. """ pass + @numpy_method def floor(self, x, out=None): """ Return the floor of the input, element-wise. """ pass + @numpy_method def ceil(self, x, out=None): """ Return the ceiling of the input, element-wise. """ pass + @numpy_method def trunc(self, x, out=None): """ @@ -1576,12 +1713,14 @@ class HostArrayBackend(ArrayBackend): Return the product of array elements over a given axis. """ pass + @numpy_method def sum(self, a, axis=None, dtype=None, out=None): """ Sum of array elements over a given axis. """ pass + @numpy_method def nanprod(self, a, axis=None, dtype=None, out=None): """ @@ -1589,24 +1728,28 @@ class HostArrayBackend(ArrayBackend): Not a Numbers (NaNs) as ones. """ pass + @numpy_method def nansum(self, a, axis=None, dtype=None, out=None): """ Return the sum of array elements over a given axis treating Not a Numbers (NaNs) as zero. """ pass + @numpy_method def cumprod(self, a, axis=None, dtype=None, out=None): """ Return the cumulative product of elements along a given axis. """ pass + @numpy_method def cumsum(self, a, axis=None, dtype=None, out=None): """ Return the cumulative sum of the elements along a given axis. """ pass + @numpy_method def nancumprod(self, a, axis=None, dtype=None, out=None): """ @@ -1614,6 +1757,7 @@ class HostArrayBackend(ArrayBackend): Not a Numbers (NaNs) as one. """ pass + @numpy_method def nancumsum(self, a, axis=None, dtype=None, out=None): """ @@ -1621,30 +1765,35 @@ class HostArrayBackend(ArrayBackend): Not a Numbers (NaNs) as zero. """ pass + @numpy_method def diff(self, a, n=1, axis=-1): """ Calculate the n-th discrete difference along given axis. """ pass + @numpy_method def ediff1d(self, ary, to_end=None, to_begin=None): """ The differences between consecutive elements of an array. """ pass + @numpy_method def gradient(self, f, *varargs, **kwargs): """ Return the gradient of an N-dimensional array. """ pass + @numpy_method def cross(self, a, b, axisa=-1, axisb=-1, axisc=-1, axis=None): """ Return the cross product of two (arrays of) vectors. """ pass + @numpy_method def trapz(self, y, x=None, dx=1.0, axis=-1): """ @@ -1659,12 +1808,14 @@ class HostArrayBackend(ArrayBackend): Calculate the exponential of all elements in the input array. """ pass + @numpy_method def exp2(self, x, out=None): """ Calculate 2**p for all p in the input array. """ pass + @numpy_method def expm1(self, x, out=None): """ @@ -1678,18 +1829,21 @@ class HostArrayBackend(ArrayBackend): Natural logarithm, element-wise. """ pass + @numpy_method def log2(self, x, out=None): """ Base-2 logarithm of x. """ pass + @numpy_method def log10(self, x, out=None): """ Return the base 10 logarithm of the input array, element-wise. """ pass + @numpy_method def log1p(self, x, out=None): """ @@ -1703,6 +1857,7 @@ class HostArrayBackend(ArrayBackend): Logarithm of the sum of exponentiations of the inputs. """ pass + @numpy_method def logaddexp2(self, x1, x2, out=None): """ @@ -1717,6 +1872,7 @@ class HostArrayBackend(ArrayBackend): Modified Bessel function of the first kind, order 0. """ pass + @numpy_method def sinc(self, x): """ @@ -1731,18 +1887,21 @@ class HostArrayBackend(ArrayBackend): Returns element-wise True where signbit is set (less than zero). """ pass + @numpy_method def copysign(self, x1, x2, out=None): """ Change the sign of x1 to that of x2, element-wise. """ pass + @numpy_method def frexp(self, x, out1=None, out2=None): """ Decompose the elements of x into mantissa and twos exponent. """ pass + @numpy_method def ldexp(self, x1, x2, out=None): """ @@ -1758,66 +1917,77 @@ class HostArrayBackend(ArrayBackend): Add arguments element-wise. """ pass + @numpy_method def reciprocal(self, x, out=None): """ Return the reciprocal of the argument, element-wise. """ pass + @numpy_method def negative(self, x, out=None): """ Numerical negative, element-wise. """ pass + @numpy_method def multiply(self, x1, x2, out=None): """ Multiply arguments element-wise. """ pass + @numpy_method def divide(self, x1, x2, out=None): """ Divide arguments element-wise. """ pass + @numpy_method def power(self, x1, x2, out=None): """ First array elements raised to powers from second array, element-wise. """ pass + @numpy_method def subtract(self, x1, x2, out=None): """ Subtract arguments, element-wise. """ pass + @numpy_method def true_divide(self, x1, x2, out=None): """ Returns a true division of the inputs, element-wise. """ pass + @numpy_method def floor_divide(self, x1, x2, out=None): """ Return the largest integer smaller or equal to the division of the inputs. """ pass + @numpy_method def fmod(self, x1, x2, out=None): """ Return the element-wise remainder of division. """ pass + @numpy_method def mod(self, x1, x2, out=None): """ Return element-wise remainder of division. """ pass + @numpy_method def modf(self, x, out1=None, out2=None): """ @@ -1832,18 +2002,21 @@ class HostArrayBackend(ArrayBackend): Return the angle of the complex argument. """ pass + @numpy_method def real(self, val): """ Return the real part of the elements of the array. """ pass + @numpy_method def imag(self, val): """ Return the imaginary part of the elements of the array. """ pass + @numpy_method def conj(self, x, out=None): """ @@ -1858,12 +2031,14 @@ class HostArrayBackend(ArrayBackend): Returns the discrete, linear convolution of two one-dimensional sequences. """ pass + @numpy_method def clip(self, a, a_min, a_max, out=None): """ Clip (limit) the values in an array. """ pass + def clip_components(self, a, a_min, a_max, out=None): """ Clip (limit) the values in an array. @@ -1871,39 +2046,45 @@ class HostArrayBackend(ArrayBackend): assert is_complex(a) if (out is None): out = np.empty_like(a) - np.clip(a.real, a_min,a_max, out.real) - np.clip(a.imag, a_min,a_max, out.imag) + np.clip(a.real, a_min, a_max, out.real) + np.clip(a.imag, a_min, a_max, out.imag) return out + @numpy_method def sqrt(self, x, out=None): """ Return the positive square-root of an array, element-wise. """ pass + @numpy_method def cbrt(self, x, out=None): """ Return the cube-root of an array, element-wise. """ pass + @numpy_method def square(self, x, out=None): """ Return the element-wise square of the input. """ pass + @numpy_method def nan_to_num(self, x): """ Replace nan with zero and inf with finite numbers. """ pass + @numpy_method def real_if_close(self, a, tol=100): """ If complex input returns a real array if complex parts are close to zero. """ pass + @numpy_method def interp(self, x, xp, fp, left=None, right=None, period=None): """ @@ -1917,30 +2098,35 @@ class HostArrayBackend(ArrayBackend): Element-wise maximum of array elements. """ pass + @numpy_method def minimum(self, x1, x2, out=None): """ Element-wise minimum of array elements. """ pass + @numpy_method def fmin(self, x1, x2, out=None): """ Element-wise maximum of array elements, ignore NaNs. """ pass + @numpy_method def fmax(self, x1, x2, out=None): """ Element-wise minimum of array elements, ignore NaNs. """ pass + @numpy_method def fabs(self, x, out=None): """ Calculate the absolute value element-wise, outputs HYSOP_REAL unless out is set. """ pass + @numpy_method def absolute(self, x, out=None): """ @@ -1958,10 +2144,11 @@ class HostArrayBackend(ArrayBackend): ################### # RANDOM SAMPLING # -## See https://docs.scipy.org/doc/numpy/reference/routines.random.html +# See https://docs.scipy.org/doc/numpy/reference/routines.random.html # Simple random data + def rand(self, shape=None, out=None): """ Random values in a given shape. @@ -1969,7 +2156,7 @@ class HostArrayBackend(ArrayBackend): if (out is not None): shape = out.shape else: - shape=tuple(shape) + shape = tuple(shape) handle = np.random.rand(*shape) if (out is not None): out[...] = handle @@ -1984,48 +2171,56 @@ class HostArrayBackend(ArrayBackend): from hysop.backend.host.host_array import HostArray handle = np.random.randn(*args) return self.wrap(handle) + @numpy_method def randint(self, low, high=None, size=None, dtype=HYSOP_INTEGER): """ Return random integers from low (inclusive) to high (exclusive). """ pass + @numpy_method def random_integers(self, low, high=None, size=None): """ Random integers of type np.int between low and high, inclusive. """ pass + @numpy_method def random_sample(self, size=None): """ Return random floats in the half-open interval 0.0, 1.0). """ pass + @numpy_method def random(self, size=None): """ Return random floats in the half-open interval 0.0, 1.0). """ pass + @numpy_method def ranf(self, size=None): """ Return random floats in the half-open interval 0.0, 1.0). """ pass + @numpy_method def sample(self, size=None): """ Return random floats in the half-open interval 0.0, 1.0). """ pass + @numpy_method def choice(self, a, size=None, replace=True, p=None): """ Generates a random sample from a given 1-D array """ pass + @numpy_method def bytes(self, length): """ @@ -2040,6 +2235,7 @@ class HostArrayBackend(ArrayBackend): Modify a sequence in-place by shuffling its contents. """ pass + @numpy_method def permutation(self, x): """ @@ -2054,204 +2250,238 @@ class HostArrayBackend(ArrayBackend): Draw samples from a Beta distribution. """ pass + @numpy_method def binomial(self, n, p, size=None): """ Draw samples from a binomial distribution. """ pass + @numpy_method def chisquare(self, df, size=None): """ Draw samples from a chi-square distribution. """ pass + @numpy_method def dirichlet(self, alpha, size=None): """ Draw samples from the Dirichlet distribution. """ pass + @numpy_method def exponential(self, scale=1.0, size=None): """ Draw samples from an exponential distribution. """ pass + @numpy_method def f(self, dfnum, dfden, size=None): """ Draw samples from an F distribution. """ pass + @numpy_method def gamma(self, shape, scale=1.0, size=None): """ Draw samples from a Gamma distribution. """ pass + @numpy_method def geometric(self, p, size=None): """ Draw samples from the geometric distribution. """ pass + @numpy_method def gumbel(self, loc=0.0, scale=1.0, size=None): """ Draw samples from a Gumbel distribution. """ pass + @numpy_method def hypergeometric(self, ngood, nbad, nsample, size=None): """ Draw samples from a Hypergeometric distribution. """ pass + @numpy_method def laplace(self, loc=0.0, scale=1.0, size=None): """ Draw samples from the Laplace or double exponential distribution with specified location (or mean=0.0) and scale (decay). """ pass + @numpy_method def logistic(self, loc=0.0, scale=1.0, size=None): """ Draw samples from a logistic distribution. """ pass + @numpy_method def lognormal(self, mean=0.0, sigma=1.0, size=None): """ Draw samples from a log-normal distribution. """ pass + @numpy_method def logseries(self, p, size=None): """ Draw samples from a logarithmic series distribution. """ pass + @numpy_method def multinomial(self, n, pvals, size=None): """ Draw samples from a multinomial distribution. """ pass + @numpy_method def multivariate_normal(self, mean, cov, size=None): """ Draw random samples from a multivariate normal distribution. """ pass + @numpy_method def negative_binomial(self, n, p, size=None): """ Draw samples from a negative binomial distribution. """ pass + @numpy_method def noncentral_chisquare(self, df, nonc, size=None): """ Draw samples from a noncentral chi-square distribution. """ pass + @numpy_method def noncentral_f(self, dfnum, dfden, nonc, size=None): """ Draw samples from the noncentral F distribution. """ pass + @numpy_method def normal(self, loc=0.0, scale=1.0, size=None): """ Draw random samples from a normal (Gaussian) distribution. """ pass + @numpy_method def pareto(self, a, size=None): """ Draw samples from a Pareto II or Lomax distribution with specified shape. """ pass + @numpy_method def poisson(self, lam, size=None): """ Draw samples from a Poisson distribution. """ pass + @numpy_method def power(self, a, size=None): """ Draws samples in 0, 1 from a power distribution with positive exponent a - 1. """ pass + @numpy_method def rayleigh(self, scale=1.0, size=None): """ Draw samples from a Rayleigh distribution. """ pass + @numpy_method def standard_cauchy(self, size=None): """ Draw samples from a standard Cauchy distribution with mode = 0. """ pass + @numpy_method def standard_exponential(self, size=None): """ Draw samples from the standard exponential distribution. """ pass + @numpy_method def standard_gamma(self, shape, size=None): """ Draw samples from a standard Gamma distribution. """ pass + @numpy_method def standard_normal(self, size=None): """ Draw samples from a standard Normal distribution (mean=0.0, stdev=1). """ pass + @numpy_method def standard_t(self, df, size=None): """ Draw samples from a standard Student's t distribution with df degrees of freedom. """ pass + @numpy_method def triangular(self, left, mode, right, size=None): """ Draw samples from the triangular distribution over the interval left, right. """ pass + @numpy_method def uniform(self, low, high, size=None): """ Draw samples from a uniform distribution. """ pass + @numpy_method def vonmises(self, mu, kappa, size=None): """ Draw samples from a von Mises distribution. """ pass + @numpy_method def wald(self, mean=0.0, scale=1.0, size=None): """ Draw samples from a Wald, or inverse Gaussian, distribution. """ pass + @numpy_method def weibull(self, a, size=None): """ Draw samples from a Weibull distribution. """ pass + @numpy_method def zipf(self, a, size=None): """ @@ -2266,12 +2496,14 @@ class HostArrayBackend(ArrayBackend): Seed the generator. """ pass + @numpy_method def get_state(self): """ Return a tuple representing the internal state of the generator. """ pass + @numpy_method def set_state(self, state): """ @@ -2282,7 +2514,7 @@ class HostArrayBackend(ArrayBackend): ################ # SET ROUTINES # -## See https://docs.scipy.org/doc/numpy/reference/routines.set.html +# See https://docs.scipy.org/doc/numpy/reference/routines.set.html # Making proper sets @numpy_method @@ -2299,24 +2531,28 @@ class HostArrayBackend(ArrayBackend): Test whether each element of a 1-D array is also present in a second array. """ pass + @numpy_method def intersect1d(self, ar1, ar2, assume_unique=False): """ Find the intersection of two arrays. """ pass + @numpy_method def setdiff1d(self, ar1, ar2, assume_unique=False): """ Find the set difference of two arrays. """ pass + @numpy_method def setxor1d(self, ar1, ar2, assume_unique=False): """ Find the set exclusive-or of two arrays. """ pass + @numpy_method def union1d(self, ar1, ar2): """ @@ -2327,7 +2563,7 @@ class HostArrayBackend(ArrayBackend): ################################### # SORTING, SEARCHING AND COUNTING # -## See https://docs.scipy.org/doc/numpy/reference/routines.sort.html +# See https://docs.scipy.org/doc/numpy/reference/routines.sort.html # Sorting @numpy_method @@ -2336,36 +2572,42 @@ class HostArrayBackend(ArrayBackend): Return a sorted copy of an array. """ pass + @numpy_method def lexsort(self, keys, axis=-1): """ Perform an indirect sort using a sequence of keys. """ pass + @numpy_method def argsort(self, a, axis=-1, kind='quicksort', order=None): """ Returns the indices that would sort an array. """ pass + @numpy_method def msort(self, a): """ Return a copy of an array sorted along the first axis. """ pass + @numpy_method def sort_complex(self, a): """ Sort a complex array using the real part first, then the imaginary part. """ pass + @numpy_method def partition(self, a, kth, axis=-1, kind='quicksort', order=None): """ Return a partitioned copy of an array. """ pass + @numpy_method def argpartition(self, a, kth, axis=-1, kind='quicksort', order=None): """ @@ -2373,61 +2615,70 @@ class HostArrayBackend(ArrayBackend): """ pass -#Searching +# Searching @numpy_method def argmax(self, a, axis, out=None): """ Returns the indices of the maximum values along an axis. """ pass + @numpy_method def nanargmax(self, a, axis=None): """ Return the indices of the maximum values in the specified axis ignoring NaNs. """ pass + @numpy_method def argmin(self, a, axis, out=None): """ Returns the indices of the minimum values along an axis. """ pass + @numpy_method def nanargmin(self, a, axis=None): """ Return the indices of the minimum values in the specified axis ignoring NaNs. """ pass + @numpy_method def argwhere(self, a): """ Find the indices of array elements that are non-zero, grouped by element. """ pass + @numpy_method def nonzero(self, a): """ Return the indices of the elements that are non-zero. """ pass + @numpy_method def flatnonzero(self, a): """ Return indices that are non-zero in the flattened version of a. """ pass + @numpy_method def where(self, condition, x, y): """ Return elements, either from x or y, depending on condition. """ pass + @numpy_method def searchsorted(self, a, v, side='left', sorter=None): """ Find indices where elements should be inserted to maintain order. """ pass + @numpy_method def extract(self, condition, arr): """ @@ -2435,7 +2686,7 @@ class HostArrayBackend(ArrayBackend): """ pass -#Counting +# Counting @numpy_method def count_nonzero(self, a, axis=None): """ @@ -2445,49 +2696,55 @@ class HostArrayBackend(ArrayBackend): ############## # STATISTICS # -## See https://docs.scipy.org/doc/numpy/reference/routines.sort.html +# See https://docs.scipy.org/doc/numpy/reference/routines.sort.html -#Order statistics +# Order statistics @numpy_method def amin(self, a, axis=None, out=None): """ Return the minimum of an array or minimum along an axis. """ pass + @numpy_method def amax(self, a, axis=None, out=None): """ Return the maximum of an array or maximum along an axis. """ pass + @numpy_method def nanmin(self, a, axis=None, out=None): """ Return minimum of an array or minimum along an axis, ignoring any NaNs. """ pass + @numpy_method def nanmax(self, a, axis=None, out=None): """ Return the maximum of an array or maximum along an axis, ignoring any NaNs. """ pass + @numpy_method def ptp(self, a, axis=None, out=None): """ Range of values (maximum - minimum) along an axis. """ pass + @numpy_method def percentile(self, a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear'): + interpolation='linear'): """ Compute the qth percentile of the data along the specified axis. """ pass + @numpy_method def nanpercentile(self, a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear'): + interpolation='linear'): """ Compute the qth percentile of the data along the specified axis, while ignoring nan values. @@ -2501,48 +2758,56 @@ class HostArrayBackend(ArrayBackend): Compute the median along the specified axis. """ pass + @numpy_method def average(self, a, axis=None, weights=None, returned=False): """ Compute the weighted average along the specified axis. """ pass + @numpy_method def mean(self, a, axis=None, dtype=None, out=None): """ Compute the arithmetic mean along the specified axis. """ pass + @numpy_method def std(self, a, axis=None, dtype=None, out=None, ddof=0): """ Compute the standard deviation along the specified axis. """ pass + @numpy_method def var(self, a, axis=None, dtype=None, out=None, ddof=0): """ Compute the variance along the specified axis. """ pass + @numpy_method def nanmedian(self, a, axis=None, out=None, overwrite_input=False): """ Compute the median along the specified axis, while ignoring NaNs. """ pass + @numpy_method def nanmean(self, a, axis=None, dtype=None, out=None): """ Compute the arithmetic mean along the specified axis, ignoring NaNs. """ pass + @numpy_method def nanstd(self, a, axis=None, dtype=None, out=None, ddof=0): """ Compute the standard deviation along the specified axis, while ignoring NaNs. """ pass + @numpy_method def nanvar(self, a, axis=None, dtype=None, out=None, ddof=0): """ @@ -2557,12 +2822,14 @@ class HostArrayBackend(ArrayBackend): Return Pearson product-moment correlation coefficients. """ pass + @numpy_method def correlate(self, a, v, mode='valid'): """ Cross-correlation of two 1-dimensional sequences. """ pass + @numpy_method def cov(self, m, y=None, rowvar=True, bias=False, ddof=None, fweights=None, aweights=None): """ @@ -2577,24 +2844,28 @@ class HostArrayBackend(ArrayBackend): Compute the histogram of a set of data. """ pass + @numpy_method def histogram2d(self, x, y, bins, range=None, normed=False, weights=None): """ Compute the bi-dimensional histogram of two data samples. """ pass + @numpy_method def histogramdd(self, sample, bins, range=None, normed=False, weights=None): """ Compute the multidimensional histogram of some data. """ pass + @numpy_method def bincount(self, x, weights=None, minlength=None): """ Count number of occurrences of each value in array of non-negative ints. """ pass + @numpy_method def digitize(self, x, bins, right=False): """ @@ -2602,5 +2873,6 @@ class HostArrayBackend(ArrayBackend): """ pass + ArrayBackend._register_backend(np.ndarray, HostArrayBackend) ArrayBackend._register_backend(HostBuffer, HostArrayBackend) diff --git a/hysop/core/arrays/tests/test_array.py b/hysop/core/arrays/tests/test_array.py index c978c5b36b25d3fa21f8c885a6861ecb52449534..9a11c8520576c79b434c7b14a85114fcfb433a6a 100644 --- a/hysop/core/arrays/tests/test_array.py +++ b/hysop/core/arrays/tests/test_array.py @@ -529,10 +529,10 @@ class TestArray(object): # FIXME OpenCl support for float16 if r0.dtype == np.float16: r1 = r1.astype(np.float16) - if r0.dtype == np.bool: - r1 = r1.astype(np.bool) + if r0.dtype == np.bool_: + r1 = r1.astype(np.bool_) - if (r0.dtype == np.bool) and (r1.dtype == np.bool): + if (r0.dtype == np.bool_) and (r1.dtype == np.bool_): l2 = np.sqrt(np.nansum(r0 ^ r1)) / r0.size linf = np.nanmax(r0 ^ r1) else: @@ -805,4 +805,3 @@ if __name__ == '__main__': test.test_host_array_backend_allocator() if __HAS_OPENCL_BACKEND__: test.test_opencl_array_backend_allocator() - diff --git a/hysop/core/memory/memory_request.py b/hysop/core/memory/memory_request.py index e4b93a61bab9b8b1de95cd53f1b21a784bf5242f..6ff03f07dae04b6290669e9fb5a3d578253884e5 100644 --- a/hysop/core/memory/memory_request.py +++ b/hysop/core/memory/memory_request.py @@ -9,15 +9,16 @@ from hysop.tools.misc import prod from hysop.core.arrays.array_backend import ArrayBackend from hysop.constants import HYSOP_BOOL, Backend + class MemoryRequest(object): """Memory request that can be requested in get_work_properties()""" __slots__ = ('backend', 'alignment', 'dtype', 'size', 'shape', 'nb_components', 'id') def __init__(self, backend, - size=None, shape=None, - dtype=None, alignment=None, - nb_components=1): + size=None, shape=None, + dtype=None, alignment=None, + nb_components=1): """ Creates a memory request to be served from given backend. @@ -30,14 +31,14 @@ class MemoryRequest(object): Every np.bool request is converted to HYSOP_BOOL dtype (ie. some integer type). """ - if dtype == np.bool: + if dtype == np.bool_: dtype = HYSOP_BOOL check_instance(backend, ArrayBackend) - check_instance(size, (int,np.integer), allow_none=True) - check_instance(alignment, (int,np.integer), allow_none=True) - check_instance(nb_components, (int,np.integer), allow_none=True) - check_instance(shape, (tuple,list,np.ndarray), allow_none=True) + check_instance(size, (int, np.integer), allow_none=True) + check_instance(alignment, (int, np.integer), allow_none=True) + check_instance(nb_components, (int, np.integer), allow_none=True) + check_instance(shape, (tuple, list, np.ndarray), allow_none=True) if (dtype is None): dtype = np.dtype(np.uint8) @@ -50,57 +51,57 @@ class MemoryRequest(object): if (size is not None) and (shape is not None): pass if (size is not None): - if size<1: + if size < 1: raise ValueError('size < 1.') shape = (size,) elif (shape is not None): size = 1 for s in shape: - if s<1: + if s < 1: raise ValueError('shape {} < 1'.format(shape)) size *= s else: raise ValueError('size and shape have not been specified.') - dtype_alignment = self.min_dtype_alignment(dtype) + dtype_alignment = self.min_dtype_alignment(dtype) hardware_alignment = self.min_hardware_alignment(backend) - alignment = first_not_None(alignment, hardware_alignment) + alignment = first_not_None(alignment, hardware_alignment) - min_alignment = min(hardware_alignment, dtype_alignment, alignment) - max_alignment = max(hardware_alignment, dtype_alignment, alignment) + min_alignment = min(hardware_alignment, dtype_alignment, alignment) + max_alignment = max(hardware_alignment, dtype_alignment, alignment) if (max_alignment % min_alignment != 0): - msg = 'Incompatible aligmnents, specified an alignment of {} ' + msg = 'Incompatible aligmnents, specified an alignment of {} ' msg += 'but given dtype should be aligned on {} bytes.' msg = msg.format(alignment, dtype_alignment) raise ValueError(msg) alignment = max_alignment if prod(shape) != size: - msg='Shape does not match size (size={}, prod(shape)={}).' + msg = 'Shape does not match size (size={}, prod(shape)={}).' msg.format(size, prod(shape)) raise ValueError(msg) if alignment <= 0: msg = 'Alignment should be positive (got {}).'.format(alignment) raise ValueError(alignment) - if (alignment&(alignment-1)!=0): + if (alignment & (alignment-1) != 0): msg = 'Alignment is not a power of two (got {}).'.format(alignment) raise ValueError(alignment) - self.backend = backend - self.alignment = alignment - self.dtype = dtype - self.size = size - self.shape = tuple(shape) + self.backend = backend + self.alignment = alignment + self.dtype = dtype + self.size = size + self.shape = tuple(shape) self.nb_components = nb_components - max_bytes = self.max_bytes() + max_bytes = self.max_bytes() max_alloc_size = self.max_alloc_size(backend) if (max_bytes > max_alloc_size): - msg='Memory request size {} ({}B) exceeds maximal allocatable size {} ({}B) ' - msg+='for backend {}.' - msg=msg.format(bytes2str(max_bytes), max_bytes, - bytes2str(max_alloc_size), max_alloc_size, - backend.full_tag) + msg = 'Memory request size {} ({}B) exceeds maximal allocatable size {} ({}B) ' + msg += 'for backend {}.' + msg = msg.format(bytes2str(max_bytes), max_bytes, + bytes2str(max_alloc_size), max_alloc_size, + backend.full_tag) raise RuntimeError(msg) def data_bytes_per_component(self): @@ -108,26 +109,31 @@ class MemoryRequest(object): Bytes to be allocated per components. """ return self.size * self.bytes_per_element(self.dtype) + def min_bytes(self): """ Minimum number bytes that may be allocated for all components. """ return self.nb_components*(self.data_bytes_per_component()) + def max_bytes(self): """ Real number bytes that will be allocated for all components. """ return self.nb_components*(self.data_bytes_per_component() + self.alignment - 1) + def max_bytes_per_component(self): """ Real number bytes that will be allocated for all components. """ return (self.data_bytes_per_component() + self.alignment - 1) - def min_dtype_alignment(self,dtype): + + def min_dtype_alignment(self, dtype): """ Returns the minimum alignement of the allocated buffer (in bytes). """ return self.bytes_per_element(dtype) + def min_hardware_alignment(self, backend): """ Returns the minimum alignement to be hardware aligned (in bytes). @@ -135,12 +141,14 @@ class MemoryRequest(object): if backend.kind == Backend.OPENCL: return backend.cl_env.device.mem_base_addr_align else: - return 8 # 64 bits by default - def bytes_per_element(self,dtype): + return 8 # 64 bits by default + + def bytes_per_element(self, dtype): """ Returns the size in bytes of the allocated data type. """ return dtype.itemsize + def max_alloc_size(self, backend): """ Returns the maximal alloc size supported by backend. @@ -149,29 +157,29 @@ class MemoryRequest(object): @classmethod def cartesian_dfield_like(cls, name, dfield, - nb_components=None, initial_values=None, dtype=None, - grid_resolution=None, ghosts=None, - backend=None, is_read_only=None): + nb_components=None, initial_values=None, dtype=None, + grid_resolution=None, ghosts=None, + backend=None, is_read_only=None): from hysop.fields.cartesian_discrete_field import CartesianDiscreteScalarFieldView check_instance(dfield, CartesianDiscreteScalarFieldView) ghosts = first_not_None(ghosts, dfield.ghosts) if np.isscalar(ghosts): - ghosts=(ghosts,)*dfield.dim + ghosts = (ghosts,)*dfield.dim elif isinstance(ghosts, bool): ghosts = dfield.ghosts if (ghosts is True) else (0,)*dfield.dim - assert len(ghosts)==dfield.dim + assert len(ghosts) == dfield.dim ghosts = np.asarray(ghosts) (dfield, request, request_id) = dfield.tmp_dfield_like(name=name, backend=backend, - nb_components=nb_components, initial_values=initial_values, dtype=dtype, - grid_resolution=grid_resolution, ghosts=ghosts, is_read_only=is_read_only) + nb_components=nb_components, initial_values=initial_values, dtype=dtype, + grid_resolution=grid_resolution, ghosts=ghosts, is_read_only=is_read_only) return (dfield, request, request_id) def stuple(self): - if not hasattr(self,'id'): - id='None' + if not hasattr(self, 'id'): + id = 'None' else: id = self.id size = bytes2str(self.min_bytes(), decimal=False) @@ -179,20 +187,20 @@ class MemoryRequest(object): return tuple(map(str, ret)) def __str__(self): - if not hasattr(self,'id'): - id='None' + if not hasattr(self, 'id'): + id = 'None' else: id = self.id - msg= 'request of size {:<9} (ncomp={}, shape={:<12}, ' - msg+='dtype={:<8}, align={:<2}, id={})' - msg=msg.format(bytes2str(self.min_bytes(), decimal=False), - self.nb_components, self.shape, - self.dtype, self.alignment, id) + msg = 'request of size {:<9} (ncomp={}, shape={:<12}, ' + msg += 'dtype={:<8}, align={:<2}, id={})' + msg = msg.format(bytes2str(self.min_bytes(), decimal=False), + self.nb_components, self.shape, + self.dtype, self.alignment, id) return msg @classmethod def empty_like(cls, a, backend=None, alignment=None, - dtype=None, size=None, shape=None, nb_components=None): + dtype=None, size=None, shape=None, nb_components=None): if hasattr(a, 'backend'): backend = first_not_None(backend, a.backend) @@ -214,8 +222,8 @@ class MemoryRequest(object): nb_components = first_not_None(nb_components, a.nb_components) return MemoryRequest(backend=backend, alignment=alignment, - dtype=dtype, size=size, shape=shape, - nb_components=nb_components) + dtype=dtype, size=size, shape=shape, + nb_components=nb_components) def __call__(self, op=None, request_identifier=None): """ @@ -230,9 +238,10 @@ class OperatorMemoryRequests(object): """ Set of memory requests originating from one operator, sorted by backend. """ + def __init__(self, operator): self._operator = operator - self._requests_per_backend = {} + self._requests_per_backend = {} self._requests_per_identifier = {} def push_mem_request(self, request_identifier, mem_request): @@ -242,7 +251,7 @@ class OperatorMemoryRequests(object): cls = mem_request.__class__.__name__ raise ValueError('Input is not a MemoryRequest (got a {}).'.format(cls)) backend = mem_request.backend - mem_request.id = request_identifier + mem_request.id = request_identifier if backend not in self._requests_per_backend: self._requests_per_backend[backend] = [] self._requests_per_backend[backend].append(mem_request) @@ -250,7 +259,7 @@ class OperatorMemoryRequests(object): mem_request.id = request_identifier def min_bytes_to_allocate(self, backend): - return sum( [req.max_bytes() for req in self._requests_per_backend[backend]] ) + return sum([req.max_bytes() for req in self._requests_per_backend[backend]]) def __call__(self): """ @@ -266,6 +275,7 @@ class MultipleOperatorMemoryRequests(object): """ Set of memory requests originating from one or more operators. """ + def __init__(self): self._allocated_buffers = {} self._all_requests_per_backend = {} @@ -280,7 +290,7 @@ class MultipleOperatorMemoryRequests(object): self._all_requests_per_backend[backend] = {} for (op, op_reqs) in op_requests.items(): if op in self._all_requests_per_backend[backend].keys(): - msg='Operator {} has already requested memory.'.format(op) + msg = 'Operator {} has already requested memory.'.format(op) raise ValueError(msg) self._all_requests_per_backend[backend][op] = op_reqs elif isinstance(mem_requests, OperatorMemoryRequests): @@ -289,12 +299,12 @@ class MultipleOperatorMemoryRequests(object): if backend not in self._all_requests_per_backend.keys(): self._all_requests_per_backend[backend] = {} if operator in self._all_requests_per_backend[backend].keys(): - msg='Operator {} has already requested memory.'.format(operator) + msg = 'Operator {} has already requested memory.'.format(operator) raise ValueError(msg) self._all_requests_per_backend[backend][operator] = requests else: cls = mem_requests.__class__ - msg='Input is not an OperatorMemoryRequests (got a {}).'.format(cls) + msg = 'Input is not an OperatorMemoryRequests (got a {}).'.format(cls) raise ValueError(msg) return self @@ -312,8 +322,8 @@ class MultipleOperatorMemoryRequests(object): def min_bytes_to_allocate(self, backend): max_bytes = 0 for mem_requests in self._all_requests_per_backend[backend].values(): - req_bytes = sum( [req.max_bytes() for req in mem_requests] ) - max_bytes = max(req_bytes,max_bytes) + req_bytes = sum([req.max_bytes() for req in mem_requests]) + max_bytes = max(req_bytes, max_bytes) return max_bytes def allocate(self, allow_subbuffers): @@ -338,7 +348,7 @@ class MultipleOperatorMemoryRequests(object): if allow_subbuffers: data = backend.empty(shape=(total_bytes,), dtype=np.uint8) - for (op,requests) in op_requests.items(): + for (op, requests) in op_requests.items(): check_instance(requests, list, values=MemoryRequest) start_idx, end_idx = 0, 0 for req in requests: @@ -349,39 +359,39 @@ class MultipleOperatorMemoryRequests(object): align_offset = (-start_idx % req.alignment) start_idx += align_offset - end_idx = start_idx + size + end_idx = start_idx + size view = data[start_idx:end_idx].view(dtype=req.dtype).reshape(req.shape) req_views.append(view) if (view.base is not data.base): - msg = 'FATAL ERROR: Could not create views on data because base ' + msg = 'FATAL ERROR: Could not create views on data because base ' msg += 'differs on backend {}.' - msg=msg.format(backend.kind) + msg = msg.format(backend.kind) raise RuntimeError(msg) if (view.int_ptr != data.int_ptr + start_idx): - msg='FATAL ERROR: Point arithmetic is wrong.' - msg+=' Expected ptr: {}' - msg+=' Actual ptr: {}' - msg=msg.format(data.int_ptr+start_idx, view.int_ptr) + msg = 'FATAL ERROR: Point arithmetic is wrong.' + msg += ' Expected ptr: {}' + msg += ' Actual ptr: {}' + msg = msg.format(data.int_ptr+start_idx, view.int_ptr) raise RuntimeError(msg) if ((view.int_ptr-data.int_ptr) % req.alignment) != 0: - msg='FATAL ERROR: Could not provide requested offset alignment.' - msg=msg.format(req.alignment) + msg = 'FATAL ERROR: Could not provide requested offset alignment.' + msg = msg.format(req.alignment) raise RuntimeError(msg) start_idx = end_idx if (op not in views): views[op] = {} - if req.nb_components>=1: + if req.nb_components >= 1: views[op][req.id] = tuple(req_views) assert end_idx <= total_bytes else: buffer_sizes = [] ordered_requests = {} - for (op,requests) in op_requests.items(): + for (op, requests) in op_requests.items(): assert op not in ordered_requests check_instance(requests, list, values=MemoryRequest) op_buffer_sizes = () @@ -390,12 +400,12 @@ class MultipleOperatorMemoryRequests(object): nbytes = req.max_bytes_per_component() for i in range(req.nb_components): op_buffer_sizes += (nbytes,) - op_reqs += (req,) + op_reqs += (req,) idx = np.argsort(op_buffer_sizes, kind='mergesort')[::-1] op_buffer_sizes = tuple(op_buffer_sizes[i] for i in idx) - op_sorted_reqs = tuple(op_reqs[i] for i in idx) - for (i,size) in enumerate(op_buffer_sizes): - if (i>=len(buffer_sizes)): + op_sorted_reqs = tuple(op_reqs[i] for i in idx) + for (i, size) in enumerate(op_buffer_sizes): + if (i >= len(buffer_sizes)): buffer_sizes.append(size) else: buffer_sizes[i] = max(buffer_sizes[i], size) @@ -406,10 +416,10 @@ class MultipleOperatorMemoryRequests(object): return buffers = tuple(backend.empty(shape=(nbytes,), dtype=np.uint8) - for nbytes in buffer_sizes) + for nbytes in buffer_sizes) for (op, requests) in ordered_requests.items(): - assert len(buffers)>= len(requests) + assert len(buffers) >= len(requests) views.setdefault(op, {}) old_req = None for (buf, req) in zip(buffers, requests): @@ -437,18 +447,18 @@ class MultipleOperatorMemoryRequests(object): def get_buffer(self, operator, request_identifier, handle=False): if not self._allocated: - msg='Memory request have not been allocated yet.' + msg = 'Memory request have not been allocated yet.' raise RuntimeError(msg) if operator not in self._allocated_buffers: - msg='Operator {} did not request any extra memory. \nOperators that requested memory are:\n *{}' - msg=msg.format(operator, '\n *'.join(str(op) - for op in self._allocated_buffers.keys())) + msg = 'Operator {} did not request any extra memory. \nOperators that requested memory are:\n *{}' + msg = msg.format(operator, '\n *'.join(str(op) + for op in self._allocated_buffers.keys())) raise RuntimeError(msg) op_buffers = self._allocated_buffers[operator] if request_identifier not in op_buffers: - msg='Unknown request id {} for operator {}.' - msg+='\nValid identifiers are: ' + ','.join(str(op) for op in op_buffers.keys()) - msg=msg.format(request_identifier, operator) + msg = 'Unknown request id {} for operator {}.' + msg += '\nValid identifiers are: ' + ','.join(str(op) for op in op_buffers.keys()) + msg = msg.format(request_identifier, operator) raise ValueError(msg) buffers = op_buffers[request_identifier] if handle: @@ -463,37 +473,37 @@ class MultipleOperatorMemoryRequests(object): all_requests = {} totals = {} for (backend, backend_requests) in self._all_requests_per_backend.items(): - total=0 + total = 0 for op in sorted(backend_requests.keys(), key=lambda op: getattr(op, 'name', None)): op_requests = backend_requests[op] sop_request = all_requests.setdefault(backend, {}).setdefault(op, []) - local_total=0 + local_total = 0 try: - opname='{}'.format(op.pretty_name) + opname = '{}'.format(op.pretty_name) except AttributeError: opname = None for req in op_requests: sop_request.append((opname,)+req.stuple()) - local_total+=req.max_bytes() - if local_total>total: - total=local_total + local_total += req.max_bytes() + if local_total > total: + total = local_total totals[backend] = total if len(all_requests): sizes = {} template = '\n' - titles=('OPERATOR', 'REQUEST_ID', 'SIZE', 'COMPONENTS', 'SHAPE', 'DTYPE', 'ALIGNMENT') - for (i,k) in enumerate(titles): - k=k.lower() + titles = ('OPERATOR', 'REQUEST_ID', 'SIZE', 'COMPONENTS', 'SHAPE', 'DTYPE', 'ALIGNMENT') + for (i, k) in enumerate(titles): + k = k.lower() template += ' ' size = max(len(req[i]) for breqs in all_requests.values() - for reqs in breqs.values() for req in reqs) + for reqs in breqs.values() for req in reqs) size = max(size, len(k)) - name=k+'_len' + name = k+'_len' sizes[name] = size - template += '{:'+('<' if i==0 else '^')+'{'+name+'}}' + template += '{:'+('<' if i == 0 else '^')+'{'+name+'}}' - ss='' + ss = '' for (backend, backend_srequests) in all_requests.items(): total = totals[backend] kind = backend.kind @@ -501,15 +511,15 @@ class MultipleOperatorMemoryRequests(object): precision = ' on device {}'.format(backend.device.name.strip()) else: precision = '' - ss+= '\n {}{}:'.format(backend.full_tag, precision) - ss+= template.format(*titles, **sizes) + ss += '\n {}{}:'.format(backend.full_tag, precision) + ss += template.format(*titles, **sizes) for op in sorted(backend_srequests.keys(), key=lambda op: getattr(op, 'name', None)): sop_reqs = backend_srequests[op] for sreq in sop_reqs: - ss+= template.format(*sreq, **sizes) - ss +='\n Total extra work buffers requested: {} ({})'.format( - bytes2str(total,decimal=False), - bytes2str(total,decimal=True)) + ss += template.format(*sreq, **sizes) + ss += '\n Total extra work buffers requested: {} ({})'.format( + bytes2str(total, decimal=False), + bytes2str(total, decimal=True)) ss += '\n' return ss[1:-1] else: diff --git a/hysop/fields/continuous_field.py b/hysop/fields/continuous_field.py index e055b27c8a909ff104d71f43bb184efbe3837d5c..399f3354e1888b7b41d8617b6290e8f399ad175b 100644 --- a/hysop/fields/continuous_field.py +++ b/hysop/fields/continuous_field.py @@ -1,3 +1,5 @@ +# coding: utf-8 + """ Continuous fields description and containers. * :class:`~hysop.fields.continuous.FieldContainerI` @@ -11,20 +13,20 @@ import sympy as sm import numpy as np from abc import ABCMeta, abstractmethod -from hysop.constants import HYSOP_REAL, HYSOP_BOOL, BoundaryCondition, BoundaryConditionConfig, DirectionLabels -from hysop.tools.decorators import debug -from hysop.tools.types import check_instance, first_not_None, to_tuple -from hysop.tools.warning import HysopWarning +from hysop.constants import HYSOP_REAL, HYSOP_BOOL, BoundaryCondition, BoundaryConditionConfig, DirectionLabels +from hysop.tools.decorators import debug +from hysop.tools.types import check_instance, first_not_None, to_tuple +from hysop.tools.warning import HysopWarning from hysop.tools.handle import TaggedObject from hysop.tools.numpywrappers import npw from hysop.domain.domain import Domain from hysop.domain.box import BoxBoundaryCondition from hysop.topology.topology import Topology, TopologyState from hysop.tools.sympy_utils import nabla, partial, subscript, subscripts, \ - exponent, exponents, xsymbol + exponent, exponents, xsymbol from hysop.symbolic import SpaceSymbol from hysop.tools.interface import NamedObjectI, SymbolContainerI, \ - NamedScalarContainerI, NamedTensorContainerI + NamedScalarContainerI, NamedTensorContainerI class FieldContainerI(TaggedObject): @@ -48,12 +50,12 @@ class FieldContainerI(TaggedObject): check_instance(name, str, allow_none=False) if (shape is not None): return TensorField(domain=domain, name=name, shape=shape, **kwds) - elif ((is_vector is True) or \ + elif ((is_vector is True) or ((nb_components is not None) and (nb_components > 1))): nb_components = first_not_None(nb_components, domain.dim) assert (is_vector is not True) or (nb_components == domain.dim) return VectorField(domain=domain, name=name, - nb_components=nb_components, **kwds) + nb_components=nb_components, **kwds) else: return ScalarField(domain=domain, name=name, **kwds) @@ -67,7 +69,6 @@ class FieldContainerI(TaggedObject): def __init__(self, domain, name=None, nb_components=None, shape=None, is_vector=None, **kwds): super(FieldContainerI, self).__init__(**kwds) - @property def is_scalar(self): return (not self.is_tensor) @@ -111,8 +112,8 @@ class FieldContainerI(TaggedObject): @classmethod def from_sympy_expressions(cls, name, exprs, space_symbols, - scalar_name_prefix=None, scalar_pretty_name_prefix=None, - pretty_name=None, **kwds): + scalar_name_prefix=None, scalar_pretty_name_prefix=None, + pretty_name=None, **kwds): """ Create a field wich has the same shape as exprs, with optional names. Expressions should be of kind sympy.Expr and are converted to FieldExpression: this @@ -136,18 +137,18 @@ class FieldContainerI(TaggedObject): sname = TensorField.default_name_formatter(scalar_name_prefix, idx) if (scalar_pretty_name_prefix is not None): spname = TensorField.default_pretty_name_formatter( - scalar_pretty_name_prefix, idx) + scalar_pretty_name_prefix, idx) else: spname = TensorField.default_pretty_name_formatter( - scalar_name_prefix, idx) + scalar_name_prefix, idx) else: # names will be autogenerated from sympy expression - sname = None + sname = None spname = None fields[idx] = cls.from_sympy_expression(expr=exprs[idx], - space_symbols=space_symbols, - name=sname, pretty_name=spname, **kwds) + space_symbols=space_symbols, + name=sname, pretty_name=spname, **kwds) return TensorField.from_field_array(name=name, pretty_name=pretty_name, fields=fields) @@ -169,8 +170,8 @@ class FieldContainerI(TaggedObject): # determine domain and boundary conditions fe = FieldExpressionBuilder.to_field_expression( - expr=expr, space_symbols=space_symbols, strict=True) - kwds['domain'] = fe.domain + expr=expr, space_symbols=space_symbols, strict=True) + kwds['domain'] = fe.domain kwds['lboundaries'] = fe.lboundaries kwds['rboundaries'] = fe.rboundaries @@ -180,18 +181,17 @@ class FieldContainerI(TaggedObject): # finally return create and return the ScalarField return ScalarField(**kwds) - def gradient(self, name=None, pretty_name=None, - scalar_name_prefix=None, scalar_pretty_name_prefix=None, - directions=None, axis=-1, - space_symbols=None, - dtype=None, **kwds): + scalar_name_prefix=None, scalar_pretty_name_prefix=None, + directions=None, axis=-1, + space_symbols=None, + dtype=None, **kwds): """ Create a field capable of storing the gradient of self, possibly altered. """ - dim = self.dim # dimension of the domain - ndim = self.ndim # number of dimension of the np.ndarray + dim = self.dim # dimension of the domain + ndim = self.ndim # number of dimension of the np.ndarray frame = self.domain.frame directions = to_tuple(first_not_None(directions, range(dim))) @@ -201,17 +201,17 @@ class FieldContainerI(TaggedObject): check_instance(space_symbols, tuple, values=SpaceSymbol, size=dim, unique=True) ndirs = len(directions) - if ndim>0: - axis = (axis+ndim)%ndim + if ndim > 0: + axis = (axis+ndim) % ndim shape = self.shape[:axis+1] + (ndirs,) + self.shape[axis+1:] else: shape = (ndirs,) name = first_not_None(name, 'grad_{}'.format(self.name)) pretty_name = first_not_None(pretty_name, '{}{}'.format(nabla, - self.pretty_name)) + self.pretty_name)) - if shape==(1,): + if shape == (1,): expr = self.symbol(frame.time, *space_symbols).diff(space_symbols[directions[0]]) return self.from_sympy_expression(expr=expr, space_symbols=space_symbols, name=name, pretty_name=pretty_name, @@ -223,16 +223,16 @@ class FieldContainerI(TaggedObject): d = directions[idx[axis+1]] if self.is_tensor: exprs[idx] = self[i].symbol(frame.time, - *space_symbols).diff(space_symbols[d]) + *space_symbols).diff(space_symbols[d]) else: - assert i==(), i + assert i == (), i exprs[idx] = self.symbol(frame.time, *space_symbols).diff(space_symbols[d]) return self.from_sympy_expressions( - exprs=exprs, space_symbols=space_symbols, - name=name, pretty_name=pretty_name, - scalar_name_prefix=scalar_name_prefix, - scalar_pretty_name_prefix=scalar_pretty_name_prefix, - dtype=dtype, **kwds) + exprs=exprs, space_symbols=space_symbols, + name=name, pretty_name=pretty_name, + scalar_name_prefix=scalar_name_prefix, + scalar_pretty_name_prefix=scalar_pretty_name_prefix, + dtype=dtype, **kwds) def laplacian(self, name=None, pretty_name=None, scalar_name_prefix=None, scalar_pretty_name_prefix=None, @@ -253,21 +253,20 @@ class FieldContainerI(TaggedObject): dtype=dtype, **kwds) else: return self.from_sympy_expressions( - exprs=exprs, space_symbols=frame.coords, - name=name, pretty_name=pretty_name, - scalar_name_prefix=scalar_name_prefix, - scalar_pretty_name_prefix=scalar_pretty_name_prefix, - dtype=dtype, **kwds) + exprs=exprs, space_symbols=frame.coords, + name=name, pretty_name=pretty_name, + scalar_name_prefix=scalar_name_prefix, + scalar_pretty_name_prefix=scalar_pretty_name_prefix, + dtype=dtype, **kwds) else: expr = exprs return self.from_sympy_expression(expr=expr, space_symbols=frame.coords, name=name, pretty_name=pretty_name, dtype=dtype, **kwds) - def div(self, name=None, pretty_name=None, - scalar_name_prefix=None, scalar_pretty_name_prefix=None, - axis=-1, dtype=None, **kwds): + scalar_name_prefix=None, scalar_pretty_name_prefix=None, + axis=-1, dtype=None, **kwds): """ Create a field capable of storing the divergence of self, on chosen axis. @@ -278,9 +277,9 @@ class FieldContainerI(TaggedObject): name = first_not_None(name, 'div_{}'.format(self.name)) pretty_name = first_not_None(pretty_name, '{}⋅{}'.format(nabla, - self.pretty_name)) + self.pretty_name)) - if exprs.size in (0,1): + if exprs.size in (0, 1): expr = exprs.item() return self.from_sympy_expression(expr=expr, space_symbols=frame.coords, name=name, pretty_name=pretty_name, @@ -293,8 +292,8 @@ class FieldContainerI(TaggedObject): dtype=dtype, **kwds) def curl(self, name=None, pretty_name=None, - scalar_name_prefix=None, scalar_pretty_name_prefix=None, - dtype=None, **kwds): + scalar_name_prefix=None, scalar_pretty_name_prefix=None, + dtype=None, **kwds): """ Create a field capable of storing the curl of self, @@ -308,31 +307,30 @@ class FieldContainerI(TaggedObject): """ from hysop.symbolic.field import curl - - if (self.dim==2): - msg='Can only take curl for a 2D field with one or two components.' - assert self.nb_components in (1,2), msg - elif (self.dim==3): - msg='Can only take curl for a 3D field with three components.' + if (self.dim == 2): + msg = 'Can only take curl for a 2D field with one or two components.' + assert self.nb_components in (1, 2), msg + elif (self.dim == 3): + msg = 'Can only take curl for a 3D field with three components.' assert self.nb_components in (3,), msg else: - msg='Can only take curl for a 2D or 3D vector field.' - assert (self.dim in (2,3)), msg + msg = 'Can only take curl for a 2D or 3D vector field.' + assert (self.dim in (2, 3)), msg frame = self.domain.frame exprs = curl(self.symbol(*frame.vars), frame) name = first_not_None(name, 'curl_{}'.format(self.name)) pretty_name = first_not_None(pretty_name, '{}∧{}'.format(nabla, - self.pretty_name)) + self.pretty_name)) if isinstance(exprs, npw.ndarray): return self.from_sympy_expressions( - exprs=exprs, space_symbols=frame.coords, - name=name, pretty_name=pretty_name, - scalar_name_prefix=scalar_name_prefix, - scalar_pretty_name_prefix=scalar_pretty_name_prefix, - dtype=dtype, **kwds) + exprs=exprs, space_symbols=frame.coords, + name=name, pretty_name=pretty_name, + scalar_name_prefix=scalar_name_prefix, + scalar_pretty_name_prefix=scalar_pretty_name_prefix, + dtype=dtype, **kwds) else: return self.from_sympy_expression(expr=exprs, space_symbols=frame.coords, name=name, pretty_name=pretty_name, @@ -342,7 +340,6 @@ class FieldContainerI(TaggedObject): """See curl.""" return self.curl(*args, **kwds) - def get_attributes(self, *attrs): """ Return all matching attributes contained in self.fields, @@ -367,7 +364,7 @@ class FieldContainerI(TaggedObject): if type(a) != type(b): return False if isinstance(a, (list, tuple, set, frozenset)): - for (ai,bi) in zip(a, b): + for (ai, bi) in zip(a, b): if not are_equal(ai, bi): return False return True @@ -380,8 +377,8 @@ class FieldContainerI(TaggedObject): return False return True if isinstance(a, npw.ndarray): - return npw.array_equal(a,b) - return (a==b) + return npw.array_equal(a, b) + return (a == b) objects = self.get_attributes(*attr) obj0 = objects[0] for obj in objects[1:]: @@ -396,32 +393,39 @@ class FieldContainerI(TaggedObject): field views. """ if self.has_unique_attribute(*attr): - return self.fields[0].get_attributes(*attr)[0] - msg='{} is not unique accross contained fields.' - msg=msg.format('.'.join(str(x) for x in attr)) + return self.fields[0].get_attributes(*attr)[0] + msg = '{} is not unique accross contained fields.' + msg = msg.format('.'.join(str(x) for x in attr)) raise AttributeError(msg) def has_unique_dtype(self): """Return true if all contained discrete fields share the same dtype.""" return self.has_unique_attribute('dtype') + def has_unique_lboundaries(self): """Return true if all contained continuous fields share the same lboundaries.""" return self.has_unique_attribute("lboundaries") + def has_unique_rboundaries(self): """Return true if all contained continuous fields share the same rboundaries.""" return self.has_unique_attribute("rboundaries") + def has_unique_boundaries(self): """Return true if all contained continuous fields share the same boundaries.""" return self.has_unique_attribute("boundaries") + def has_unique_lboundaries_kind(self): """Return true if all contained continuous fields share the same lboundaries kind.""" return self.has_unique_attribute("lboundaries_kind") + def has_unique_rboundaries_kind(self): """Return true if all contained continuous fields share the same rboundaries kind.""" return self.has_unique_attribute("rboundaries_kind") + def has_unique_boundaries_kind(self): """Return true if all contained continuous fields share the same boundaries kind.""" return self.has_unique_attribute("boundaries_kind") + def has_unique_periodicity(self): """Return true if all contained continuous fields share the same periodicity.""" return self.has_unique_attribute("periodicity") @@ -433,6 +437,7 @@ class FieldContainerI(TaggedObject): else raise an AttributeError. """ return self.get_unique_attribute('dtype') + @property def lboundaries(self): """ @@ -440,6 +445,7 @@ class FieldContainerI(TaggedObject): else raise an AttributeError. """ return self.get_unique_attribute("lboundaries") + @property def rboundaries(self): """ @@ -447,6 +453,7 @@ class FieldContainerI(TaggedObject): else raise an AttributeError. """ return self.get_unique_attribute("rboundaries") + @property def boundaries(self): """ @@ -454,6 +461,7 @@ class FieldContainerI(TaggedObject): else raise an AttributeError. """ return self.get_unique_attribute("boundaries") + @property def lboundaries_kind(self): """ @@ -461,6 +469,7 @@ class FieldContainerI(TaggedObject): else raise an AttributeError. """ return self.get_unique_attribute("lboundaries_kind") + @property def rboundaries_kind(self): """ @@ -468,6 +477,7 @@ class FieldContainerI(TaggedObject): else raise an AttributeError. """ return self.get_unique_attribute("rboundaries_kind") + @property def boundaries_kind(self): """ @@ -475,6 +485,7 @@ class FieldContainerI(TaggedObject): else raise an AttributeError. """ return self.get_unique_attribute("boundaries_kind") + @property def periodicity(self): """ @@ -485,15 +496,17 @@ class FieldContainerI(TaggedObject): def __eq__(self, other): return (self is other) + def __ne__(self, other): return (self is not other) + def __hash__(self): return id(self) - def _get_domain(self): """Return the physical domain where this field is defined.""" return self._domain + def _get_dim(self): """Return the dimension of the physical domain.""" return self._dim @@ -596,10 +609,10 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): assert is_tmp, 'Can only specify mem_tag for temporary fields.' # Data type of the field - if (dtype==npw.bool) or (dtype==bool): + if (dtype == npw.bool_) or (dtype == bool): import warnings - msg='Parameter dtype=npw.bool has been converted ' - msg+='to HYSOP_BOOL={}.'.format(HYSOP_BOOL.__name__) + msg = 'Parameter dtype=npw.bool_ has been converted ' + msg += 'to HYSOP_BOOL={}.'.format(HYSOP_BOOL.__name__) warnings.warn(msg, HysopWarning) dtype = HYSOP_BOOL dtype = npw.dtype(dtype) @@ -609,40 +622,40 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): check_instance(pretty_name, str) # Initial values - if not isinstance(initial_values,(list,tuple)): + if not isinstance(initial_values, (list, tuple)): initial_values = (initial_values, initial_values) - assert len(initial_values)==2 + assert len(initial_values) == 2 initial_values = tuple(initial_values) check_instance(initial_values, tuple, size=2) # Field boundary conditions lboundaries = npw.asarray(first_not_None(lboundaries, - cls.default_boundaries_from_domain(domain.lboundaries))) + cls.default_boundaries_from_domain(domain.lboundaries))) rboundaries = npw.asarray(first_not_None(rboundaries, - cls.default_boundaries_from_domain(domain.rboundaries))) + cls.default_boundaries_from_domain(domain.rboundaries))) check_instance(lboundaries, npw.ndarray, values=(BoundaryCondition, BoundaryConditionConfig), - ndim=1, size=domain.dim, dtype=object, allow_none=True) + ndim=1, size=domain.dim, dtype=object, allow_none=True) check_instance(rboundaries, npw.ndarray, values=(BoundaryCondition, BoundaryConditionConfig), - ndim=1, size=domain.dim, dtype=object, allow_none=True) + ndim=1, size=domain.dim, dtype=object, allow_none=True) assert lboundaries.size == rboundaries.size == domain.dim - for i,(lb,rb) in enumerate(zip(lboundaries,rboundaries)): - if (lb.bc==BoundaryCondition.PERIODIC) ^ (rb.bc==BoundaryCondition.PERIODIC): - msg='Periodic BoundaryCondition mismatch on axis {}.'.format(i) + for i, (lb, rb) in enumerate(zip(lboundaries, rboundaries)): + if (lb.bc == BoundaryCondition.PERIODIC) ^ (rb.bc == BoundaryCondition.PERIODIC): + msg = 'Periodic BoundaryCondition mismatch on axis {}.'.format(i) raise ValueError(msg) check_instance(lboundaries, npw.ndarray, values=(BoundaryCondition, BoundaryConditionConfig), - ndim=1, size=domain.dim, dtype=object) + ndim=1, size=domain.dim, dtype=object) check_instance(rboundaries, npw.ndarray, values=(BoundaryCondition, BoundaryConditionConfig), - ndim=1, size=domain.dim, dtype=object) + ndim=1, size=domain.dim, dtype=object) - periodic = BoundaryCondition.PERIODIC - periodicity = np.asarray(tuple(map(lambda x: x.bc, lboundaries)))==periodic + periodic = BoundaryCondition.PERIODIC + periodicity = np.asarray(tuple(map(lambda x: x.bc, lboundaries))) == periodic kwds.pop('make_field', None) obj = super(ScalarField, cls).__new__(cls, domain=domain, - name=name, pretty_name=pretty_name, - var_name=var_name, latex_name=latex_name, - tag_prefix='f', tagged_cls=ScalarField, **kwds) - obj._dtype = dtype + name=name, pretty_name=pretty_name, + var_name=var_name, latex_name=latex_name, + tag_prefix='f', tagged_cls=ScalarField, **kwds) + obj._dtype = dtype obj._initial_values = initial_values obj._is_tmp = is_tmp obj._mem_tag = mem_tag @@ -662,33 +675,33 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): return obj def __init__(self, domain, name, pretty_name=None, - var_name=None, latex_name=None, - initial_values=None, dtype=HYSOP_REAL, - lboundaries=None, rboundaries=None, - is_tmp=False, mem_tag=None, **kwds): + var_name=None, latex_name=None, + initial_values=None, dtype=HYSOP_REAL, + lboundaries=None, rboundaries=None, + is_tmp=False, mem_tag=None, **kwds): kwds.pop('make_field', None) super(ScalarField, self).__init__(domain=domain, - name=name, pretty_name=pretty_name, - var_name=var_name, latex_name=latex_name, - tag_prefix='f', tagged_cls=ScalarField, **kwds) + name=name, pretty_name=pretty_name, + var_name=var_name, latex_name=latex_name, + tag_prefix='f', tagged_cls=ScalarField, **kwds) @classmethod def default_boundaries_from_domain(cls, boundaries): check_instance(boundaries, npw.ndarray, values=BoxBoundaryCondition) field_boundaries = npw.empty_like(boundaries) field_boundaries[...] = None - for (i,bd) in enumerate(boundaries): + for (i, bd) in enumerate(boundaries): if (bd is BoxBoundaryCondition.PERIODIC): fbd = BoundaryCondition.PERIODIC - elif (bd is BoxBoundaryCondition.SYMMETRIC): # (normal to boundary velocity = 0) + elif (bd is BoxBoundaryCondition.SYMMETRIC): # (normal to boundary velocity = 0) # let any advected scalar to be 0 in boundaries fbd = BoundaryCondition.HOMOGENEOUS_DIRICHLET - elif (bd is BoxBoundaryCondition.OUTFLOW): # (velocity normal to boundary) + elif (bd is BoxBoundaryCondition.OUTFLOW): # (velocity normal to boundary) # let any advected scalar to go trough the boundary fbd = BoundaryCondition.HOMOGENEOUS_NEUMANN else: - msg='FATAL ERROR: Unknown domain boundary condition {}.' - msg=msg.format(bd) + msg = 'FATAL ERROR: Unknown domain boundary condition {}.' + msg = msg.format(bd) raise NotImplementedError(msg) field_boundaries[i] = fbd return field_boundaries @@ -705,31 +718,31 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): check_instance(obj.discrete_fields, dict) check_instance(obj.initial_values, tuple, size=2) check_instance(obj.lboundaries, npw.ndarray, values=(BoundaryCondition, BoundaryConditionConfig), - ndim=1, size=obj.domain.dim, dtype=object) + ndim=1, size=obj.domain.dim, dtype=object) check_instance(obj.rboundaries, npw.ndarray, values=(BoundaryCondition, BoundaryConditionConfig), - ndim=1, size=obj.domain.dim, dtype=object) + ndim=1, size=obj.domain.dim, dtype=object) check_instance(obj.periodicity, npw.ndarray, dtype=bool, - ndim=1, size=obj.domain.dim) + ndim=1, size=obj.domain.dim) check_instance(obj.is_tmp, bool) def field_like(self, name, pretty_name=None, - latex_name=None, var_name=None, - domain=None, dtype=None, is_tmp=None, - lboundaries=None, rboundaries=None, - initial_values=None, **kwds): + latex_name=None, var_name=None, + domain=None, dtype=None, is_tmp=None, + lboundaries=None, rboundaries=None, + initial_values=None, **kwds): """Create a ScalarField like this object, possibly altered.""" check_instance(name, str) - domain = first_not_None(domain, self.domain) - dtype = first_not_None(dtype, self.dtype) - is_tmp = first_not_None(is_tmp, self.is_tmp) - lboundaries = first_not_None(lboundaries, self.lboundaries) - rboundaries = first_not_None(rboundaries, self.rboundaries) + domain = first_not_None(domain, self.domain) + dtype = first_not_None(dtype, self.dtype) + is_tmp = first_not_None(is_tmp, self.is_tmp) + lboundaries = first_not_None(lboundaries, self.lboundaries) + rboundaries = first_not_None(rboundaries, self.rboundaries) initial_values = first_not_None(initial_values, self.initial_values) return ScalarField(name=name, pretty_name=pretty_name, - var_name=var_name, latex_name=latex_name, - domain=domain, dtype=dtype, is_tmp=is_tmp, - lboundaries=lboundaries, rboundaries=rboundaries, - initial_values=initial_values, **kwds) + var_name=var_name, latex_name=latex_name, + domain=domain, dtype=dtype, is_tmp=is_tmp, + lboundaries=lboundaries, rboundaries=rboundaries, + initial_values=initial_values, **kwds) def tmp_like(self, name, **kwds): """Create a TemporaryField like self, possibly altered.""" @@ -751,7 +764,7 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): def long_description(self): """Long description of this field.""" s = textwrap.dedent( - ''' + ''' {} *name: {} *pretty_name: {} @@ -764,15 +777,14 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): *initial values: {} *topology tags: [{}] ''').format(self.full_tag, - self.name, self.pretty_name, - self.var_name, self.latex_name, - self.dim, self.dtype, - self.lboundaries.tolist(), self.rboundaries.tolist(), - self.initial_values, - ','.join([k.full_tag for k in self.discrete_fields.keys()])) + self.name, self.pretty_name, + self.var_name, self.latex_name, + self.dim, self.dtype, + self.lboundaries.tolist(), self.rboundaries.tolist(), + self.initial_values, + ','.join([k.full_tag for k in self.discrete_fields.keys()])) return s[1:] - @debug def discretize(self, topology, topology_state=None): """ @@ -812,39 +824,50 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): def _get_dtype(self): """Return the default allocation dtype of this ScalarField.""" return self._dtype + def _get_initial_values(self): """Return initial value of this field (compute_val, ghost_val).""" return self._initial_values + def _get_discrete_fields(self): """ Return the dictionnary containing all the discretizations of this field. """ return self._discrete_fields + def _get_lboundaries(self): """Left boundary conditions.""" return self._lboundaries + def _get_rboundaries(self): """Right boundary conditions.""" return self._rboundaries + def _get_lboundaries_kind(self): """Left boundary condition kind.""" return np.asarray(tuple(map(lambda x: x.bc, self._lboundaries))) + def _get_rboundaries_kind(self): """Right boundary condition kind.""" return np.asarray(tuple(map(lambda x: x.bc, self._rboundaries))) + def _get_boundaries(self): """Left and right boundary conditions as a tuple.""" return (self._lboundaries, self._rboundaries) + def _get_boundaries_kind(self): """Left and right boundary condition kind as a tuple.""" return (self.lboundaries_kind, self._get_lboundaries_kind) + def _get_periodicity(self): """Numpy array mask, True is axis is periodic, else False.""" return self._periodicity + def _get_is_tmp(self): """Is this ScalarField a temporary field ?""" return self._is_tmp + def _get_mem_tag(self): return self._mem_tag @@ -875,10 +898,13 @@ class ScalarField(NamedScalarContainerI, FieldContainerI): def __str__(self): return self.long_description() + def __eq__(self, other): return (self is other) + def __ne__(self, other): return (self is not other) + def __hash__(self): return id(self) @@ -905,26 +931,26 @@ class TensorField(NamedTensorContainerI, FieldContainerI): return True def __new__(cls, domain, name, shape, - pretty_name=None, - name_formatter=None, - pretty_name_formatter=None, - skip_field=None, - make_field=None, - fields=None, - base_kwds=None, **kwds): + pretty_name=None, + name_formatter=None, + pretty_name_formatter=None, + skip_field=None, + make_field=None, + fields=None, + base_kwds=None, **kwds): pretty_name = first_not_None(pretty_name, name) check_instance(name, str) check_instance(pretty_name, str) check_instance(shape, tuple, values=int) - if (len(shape)==1) and not issubclass(cls, VectorField): + if (len(shape) == 1) and not issubclass(cls, VectorField): obj = VectorField(domain=domain, shape=shape, - name=name, - name_formatter=name_formatter, - pretty_name=pretty_name, - pretty_name_formatter=pretty_name_formatter, - skip_field=skip_field, make_field=make_field, - fields=fields, base_kwds=base_kwds, **kwds) + name=name, + name_formatter=name_formatter, + pretty_name=pretty_name, + pretty_name_formatter=pretty_name_formatter, + skip_field=skip_field, make_field=make_field, + fields=fields, base_kwds=base_kwds, **kwds) return obj name_formatter = first_not_None(name_formatter, cls.default_name_formatter) @@ -936,9 +962,9 @@ class TensorField(NamedTensorContainerI, FieldContainerI): check_instance(domain, Domain) - if npw.prod(shape)<=0: - msg='Invalid shape for a tensor-like field, got {}.' - msg=msg.format(shape) + if npw.prod(shape) <= 0: + msg = 'Invalid shape for a tensor-like field, got {}.' + msg = msg.format(shape) raise ValueError(msg) if (fields is None): @@ -947,9 +973,9 @@ class TensorField(NamedTensorContainerI, FieldContainerI): if skip_field(idx): field = None else: - fname = name_formatter(basename=name, idx=idx) + fname = name_formatter(basename=name, idx=idx) pfname = pretty_name_formatter(basename=pretty_name, idx=idx) - field = make_field(idx, domain=domain, name=fname, pretty_name=pfname, **kwds) + field = make_field(idx, domain=domain, name=fname, pretty_name=pfname, **kwds) fields += (field,) cls._check_fields(*fields) fields = npw.asarray(fields, dtype=object).reshape(shape) @@ -960,9 +986,9 @@ class TensorField(NamedTensorContainerI, FieldContainerI): assert npw.array_equal(fields.shape, shape) obj = super(TensorField, cls).__new__(cls, domain=domain, - name=name, pretty_name=pretty_name, - tag_prefix='tf', tagged_cls=TensorField, - contained_objects=fields, **base_kwds) + name=name, pretty_name=pretty_name, + tag_prefix='tf', tagged_cls=TensorField, + contained_objects=fields, **base_kwds) obj._fields = fields obj._name_formatter = name_formatter obj._pretty_name_formatter = pretty_name_formatter @@ -976,18 +1002,18 @@ class TensorField(NamedTensorContainerI, FieldContainerI): return obj def __init__(self, domain, name, shape, - pretty_name=None, - name_formatter=None, - pretty_name_formatter=None, - skip_field=None, - make_field=None, - fields=None, - base_kwds=None, **kwds): + pretty_name=None, + name_formatter=None, + pretty_name_formatter=None, + skip_field=None, + make_field=None, + fields=None, + base_kwds=None, **kwds): base_kwds = first_not_None(base_kwds, {}) super(TensorField, self).__init__(domain=domain, - name=name, pretty_name=pretty_name, - tag_prefix='tf', tagged_cls=TensorField, - contained_objects=fields, **base_kwds) + name=name, pretty_name=pretty_name, + tag_prefix='tf', tagged_cls=TensorField, + contained_objects=fields, **base_kwds) def discretize(self, topology, topology_state=None): from hysop.fields.discrete_field import DiscreteTensorField @@ -1001,7 +1027,7 @@ class TensorField(NamedTensorContainerI, FieldContainerI): def from_fields(cls, name, fields, shape, pretty_name=None, **kwds): """Create a TensorField from a list of fields and a shape.""" fields = to_tuple(fields) - shape = to_tuple(shape) + shape = to_tuple(shape) check_instance(fields, tuple, values=(ScalarField,), minsize=1) check_instance(shape, tuple, values=int) @@ -1016,7 +1042,7 @@ class TensorField(NamedTensorContainerI, FieldContainerI): fields = npw.asarray(fields, dtype=object).reshape(shape) return Field(domain=domain, name=name, shape=shape, pretty_name=pretty_name, - fields=fields, **kwds) + fields=fields, **kwds) @classmethod def from_field_array(cls, name, fields, pretty_name=None, **kwds): @@ -1034,21 +1060,21 @@ class TensorField(NamedTensorContainerI, FieldContainerI): domain = field0.domain return Field(domain=domain, name=name, pretty_name=pretty_name, - shape=shape, fields=fields, **kwds) + shape=shape, fields=fields, **kwds) @classmethod def _check_fields(cls, *fields): """Check that at least one field is specified.""" field0 = first_not_None(*fields) if (field0 is None): - msg='Tensor field {} should at least contain a valid ScalarField.' - msg=msg.format(name) + msg = 'Tensor field {} should at least contain a valid ScalarField.' + msg = msg.format(name) raise ValueError(msg) @classmethod def default_name_formatter(cls, basename, idx): - assert len(basename)>0 + assert len(basename) > 0 if basename[-1] in '0123456789': sep = '_' else: @@ -1059,7 +1085,7 @@ class TensorField(NamedTensorContainerI, FieldContainerI): @classmethod def default_pretty_name_formatter(cls, basename, idx): check_instance(basename, str) - assert len(basename)>0 + assert len(basename) > 0 pname = basename + subscripts(ids=idx, sep='') return pname @@ -1076,7 +1102,7 @@ class TensorField(NamedTensorContainerI, FieldContainerI): for field in self: check_instance(field, ScalarField) if (field.domain.domain is not domain): - msg='Domain mismatch for field {}.'.format(field.name) + msg = 'Domain mismatch for field {}.'.format(field.name) raise ValueError(msg) def _check_names(self): @@ -1084,17 +1110,17 @@ class TensorField(NamedTensorContainerI, FieldContainerI): names = {} pnames = {} for field in self: - name = field.name + name = field.name pname = field.pretty_name if (name in names) and (names[name] is not field): - msg='Name {} was already used by another field.' - msg=msg.format(name) + msg = 'Name {} was already used by another field.' + msg = msg.format(name) raise ValueError(msg) if (pname in pnames) and (pretty_name[name] is not field): - msg='Name {} was already used by another field.' - msg=msg.format(pname) + msg = 'Name {} was already used by another field.' + msg = msg.format(pname) raise ValueError(msg) - names[name] = field + names[name] = field pnames[name] = field @property @@ -1119,13 +1145,13 @@ class TensorField(NamedTensorContainerI, FieldContainerI): """Short description of this tensor field.""" s = '{}[name={}, pname={}, dim={}, shape={}]' s = s.format(self.full_tag, self.name, self.pretty_name, self.dim, - self.shape) + self.shape) return s def long_description(self): """Long description of this tensor field as a string.""" - s=textwrap.dedent( - ''' + s = textwrap.dedent( + ''' {} *name: {} *pretty_name: {} @@ -1134,13 +1160,13 @@ class TensorField(NamedTensorContainerI, FieldContainerI): *nb_components: {} *symbolic repr.: '''.format(self.full_tag, - self.name, self.pretty_name, self.dim, self.shape, self.nb_components))[1:] - s+=' '+'\n '.join(str(self.symbol).split('\n')) + self.name, self.pretty_name, self.dim, self.shape, self.nb_components))[1:] + s += ' '+'\n '.join(str(self.symbol).split('\n')) return s def field_like(self, name, pretty_name=None, - shape=None, nb_components=None, - fn='field_like', **kwds): + shape=None, nb_components=None, + fn='field_like', **kwds): """Create a TensorField like this object, possibly altered.""" if (shape is None) and (nb_components is not None): shape = (nb_components,) @@ -1157,14 +1183,14 @@ class TensorField(NamedTensorContainerI, FieldContainerI): else: fields = npw.empty(shape=shape, dtype=object) if (self.shape == shape): - for (idx,field) in self.nd_iter(): - fname = self._name_formatter(basename=name, idx=idx) + for (idx, field) in self.nd_iter(): + fname = self._name_formatter(basename=name, idx=idx) pfname = self._pretty_name_formatter(basename=pretty_name, idx=idx) fields[idx] = getattr(field, fn)(name=fname, pretty_name=pfname, **kwds) else: field = self.fields[0] for idx in npw.ndindex(*shape): - fname = self._name_formatter(basename=name, idx=idx) + fname = self._name_formatter(basename=name, idx=idx) pfname = self._pretty_name_formatter(basename=pretty_name, idx=idx) fields[idx] = getattr(field, fn)(name=fname, pretty_name=pfname, **kwds) return self.from_field_array(name=name, pretty_name=pretty_name, fields=fields) @@ -1191,24 +1217,24 @@ class VectorField(TensorField): check_instance(nb_components, int, minval=1) shape = (nb_components,) check_instance(shape, tuple, values=int, size=1) - if shape[0]==1: + if shape[0] == 1: return ScalarField(domain=domain, name=name, **kwds) obj = super(VectorField, cls).__new__(cls, domain=domain, name=name, - shape=shape, **kwds) + shape=shape, **kwds) return obj def __init__(self, domain, name, nb_components=None, shape=None, **kwds): super(VectorField, self).__init__(domain=domain, name=name, - shape=shape, **kwds) + shape=shape, **kwds) @classmethod def default_name_formatter(cls, basename, idx): - assert len(basename)>0 + assert len(basename) > 0 if basename[-1] in '0123456789': sep = '_' else: sep = '' - if len(idx)==1: + if len(idx) == 1: name = basename + sep + '_'.join(DirectionLabels[i] for i in idx) else: name = basename + sep + '_'.join(str(i) for i in idx) diff --git a/hysop/tools/field_utils.py b/hysop/tools/field_utils.py index db25036710c6b7fbc270f4c9329d21a52b5bfa35..7b7b008bec1d944a74a48bab61cb81841c1b936b 100644 --- a/hysop/tools/field_utils.py +++ b/hysop/tools/field_utils.py @@ -1,22 +1,25 @@ +# coding: utf-8 + from hysop.tools.types import first_not_None, to_tuple from hysop.tools.sympy_utils import nabla, partial, subscript, subscripts, \ - exponent, exponents, xsymbol, get_derivative_variables + exponent, exponents, xsymbol, get_derivative_variables from sympy.printing.str import StrPrinter, StrReprPrinter -from sympy.printing.ccode import C99CodePrinter +from sympy.printing.c import C99CodePrinter from sympy.printing.latex import LatexPrinter + class BasePrinter(object): def print_Derivative(self, expr): (bvar, pvar, vvar, lvar) = print_all_names(expr.args[0]) pvar = pvar all_xvars = get_derivative_variables(expr) - xvars = tuple(set(all_xvars)) + xvars = tuple(set(all_xvars)) varpows = tuple(all_xvars.count(x) for x in xvars) - bxvars = tuple(print_name(x) for x in xvars) - pxvars = tuple(print_pretty_name(x) for x in xvars) - vxvars = tuple(print_var_name(x) for x in xvars) - lxvars = tuple(print_latex_name(x) for x in xvars) + bxvars = tuple(print_name(x) for x in xvars) + pxvars = tuple(print_pretty_name(x) for x in xvars) + vxvars = tuple(print_var_name(x) for x in xvars) + lxvars = tuple(print_latex_name(x) for x in xvars) return DifferentialStringFormatter.format_pd(bvar, pvar, vvar, lvar, bxvars, pxvars, vxvars, lxvars, varpows=varpows) @@ -26,8 +29,8 @@ class BasePrinter(object): return super(BasePrinter, self)._print(expr, **kwds) except: print - msg='FATAL ERROR: {} failed to print expression {}.' - msg=msg.format(type(self).__name__, expr) + msg = 'FATAL ERROR: {} failed to print expression {}.' + msg = msg.format(type(self).__name__, expr) print(msg) print raise @@ -40,18 +43,22 @@ class NamePrinter(BasePrinter, StrReprPrinter): elif hasattr(expr, '_name'): return expr._name return super(NamePrinter, self)._print(expr, **kwds) + def _print_Derivative(self, expr): return super(NamePrinter, self).print_Derivative(expr)[0] + def _print_Add(self, expr): return super(NamePrinter, self)._print_Add(expr).replace(' ', '') + def _print_Mul(self, expr): return super(NamePrinter, self)._print_Mul(expr).replace(' ', '') + def emptyPrinter(self, expr): - msg='\n{} does not implement _print_{}(self, expr).' - msg+='\nExpression is {}.'.format(expr) - msg+='\nExpression type MRO is:' - msg+='\n *'+'\n *'.join(t.__name__ for t in type(expr).__mro__) - msg=msg.format(self.__class__.__name__, expr.__class__.__name__) + msg = '\n{} does not implement _print_{}(self, expr).' + msg += '\nExpression is {}.'.format(expr) + msg += '\nExpression type MRO is:' + msg += '\n *'+'\n *'.join(t.__name__ for t in type(expr).__mro__) + msg = msg.format(self.__class__.__name__, expr.__class__.__name__) raise NotImplementedError(msg) @@ -62,14 +69,16 @@ class PrettyNamePrinter(BasePrinter, StrPrinter): elif hasattr(expr, '_pretty_name'): return expr._pretty_name return super(PrettyNamePrinter, self)._print(expr, **kwds) + def _print_Derivative(self, expr): return super(PrettyNamePrinter, self).print_Derivative(expr)[1] + def emptyPrinter(self, expr): - msg='\n{} does not implement _print_{}(self, expr).' - msg+='\nExpression is {}.'.format(expr) - msg+='\nExpression type MRO is:' - msg+='\n *'+'\n *'.join(t.__name__ for t in type(expr).__mro__) - msg=msg.format(self.__class__.__name__, expr.__class__.__name__) + msg = '\n{} does not implement _print_{}(self, expr).' + msg += '\nExpression is {}.'.format(expr) + msg += '\nExpression type MRO is:' + msg += '\n *'+'\n *'.join(t.__name__ for t in type(expr).__mro__) + msg = msg.format(self.__class__.__name__, expr.__class__.__name__) raise NotImplementedError(msg) @@ -80,23 +89,27 @@ class VarNamePrinter(BasePrinter, C99CodePrinter): elif hasattr(expr, '_var_name'): return expr._var_name return super(VarNamePrinter, self)._print(expr, **kwds).replace(' ', '') + def _print_Derivative(self, expr): return super(VarNamePrinter, self).print_Derivative(expr)[2] + def _print_Add(self, expr): s = super(VarNamePrinter, self)._print_Add(expr) s = s.replace(' + ', '_plus_').replace(' - ', '_minus_') s = s.replace('+', 'plus_').replace('-', 'minus_') return s + def _print_Mul(self, expr): s = super(VarNamePrinter, self)._print_Mul(expr) s = s.replace(' * ', '_times_').replace('+', 'plus_').replace('-', 'minus_') return s + def emptyPrinter(self, expr): - msg='\n{} does not implement _print_{}(self, expr).' - msg+='\nExpression is {}.'.format(expr) - msg+='\nExpression type MRO is:' - msg+='\n *'+'\n *'.join(t.__name__ for t in type(expr).__mro__) - msg=msg.format(self.__class__.__name__, expr.__class__.__name__) + msg = '\n{} does not implement _print_{}(self, expr).' + msg += '\nExpression is {}.'.format(expr) + msg += '\nExpression type MRO is:' + msg += '\n *'+'\n *'.join(t.__name__ for t in type(expr).__mro__) + msg = msg.format(self.__class__.__name__, expr.__class__.__name__) raise NotImplementedError(msg) @@ -107,35 +120,44 @@ class LatexNamePrinter(BasePrinter, LatexPrinter): elif hasattr(expr, '_latex_name'): return expr._latex_name return super(LatexNamePrinter, self)._print(expr, **kwds) + def _print_Derivative(self, expr): return super(LatexNamePrinter, self).print_Derivative(expr)[3] + def _print_int(self, expr): return str(expr) + def emptyPrinter(self, expr): - msg='\n{} does not implement _print_{}(self, expr).' - msg+='\nExpression is {}.'.format(expr) - msg+='\nExpression type MRO is:' - msg+='\n *'+'\n *'.join(t.__name__ for t in type(expr).__mro__) - msg=msg.format(self.__class__.__name__, expr.__class__.__name__) + msg = '\n{} does not implement _print_{}(self, expr).' + msg += '\nExpression is {}.'.format(expr) + msg += '\nExpression type MRO is:' + msg += '\n *'+'\n *'.join(t.__name__ for t in type(expr).__mro__) + msg = msg.format(self.__class__.__name__, expr.__class__.__name__) raise NotImplementedError(msg) + pbn = NamePrinter() ppn = PrettyNamePrinter() #pvn = VarNamePrinter() pln = LatexNamePrinter() + def print_name(expr): return pbn.doprint(expr) + def print_pretty_name(expr): return ppn.doprint(expr) + def print_var_name(expr): return VarNamePrinter().doprint(expr) + def print_latex_name(expr): return pln.doprint(expr) + def print_all_names(expr): name = print_name(expr) pretty_name = print_pretty_name(expr) @@ -145,47 +167,94 @@ def print_all_names(expr): def to_str(*args): - if len(args)==1: - args=to_tuple(args[0]) + if len(args) == 1: + args = to_tuple(args[0]) + def _to_str(x): return str(x) return tuple(_to_str(y) for y in args) + # exponents formatting functions -bexp_fn = lambda x: '^{}'.format(x) if (x>1) else '' -pexp_fn = lambda x, sep=',': exponents(x, sep=sep) if (x>1) else '' -vexp_fn = lambda x: 'e{}'.format(x) if (x>1) else '' -lexp_fn = lambda x: '^<LBRACKET>{}<RBRACKET>'.format(x) if (x>1) else '' +def bexp_fn(x): return '^{}'.format(x) if (x > 1) else '' + + +pexp_fn = lambda x, sep=',': exponents(x, sep=sep) if (x > 1) else '' + + +def vexp_fn(x): return 'e{}'.format(x) if (x > 1) else '' + + +def lexp_fn(x): return '^<LBRACKET>{}<RBRACKET>'.format(x) if (x > 1) else '' # powers formatting functions -bpow_fn = lambda x: '**{}'.format(x) if (x>1) else '' -ppow_fn = lambda x, sep=',': exponents(x,sep=sep) if (x>1) else '' -vpow_fn = lambda x: 'p{}'.format(x) if (x>1) else '' -lpow_fn = lambda x: '^<LBRACKET>{}<RBRACKET>'.format(x) if (x>1) else '' + + +def bpow_fn(x): return '**{}'.format(x) if (x > 1) else '' + + +ppow_fn = lambda x, sep=',': exponents(x, sep=sep) if (x > 1) else '' + + +def vpow_fn(x): return 'p{}'.format(x) if (x > 1) else '' + + +def lpow_fn(x): return '^<LBRACKET>{}<RBRACKET>'.format(x) if (x > 1) else '' # subcripts formatting functions -bsub_fn = lambda x: '_{}'.format(x) if (x is not None) else '' -psub_fn = lambda x, sep=',': subscripts(x,sep=sep) if (x is not None) else '' -vsub_fn = lambda x: 's{}'.format(x) if (x is not None) else '' -lsub_fn = lambda x: '_<LBRACKET>{}<RBRACKET>'.format(x) if (x is not None) else '' + + +def bsub_fn(x): return '_{}'.format(x) if (x is not None) else '' + + +psub_fn = lambda x, sep=',': subscripts(x, sep=sep) if (x is not None) else '' + + +def vsub_fn(x): return 's{}'.format(x) if (x is not None) else '' + + +def lsub_fn(x): return '_<LBRACKET>{}<RBRACKET>'.format(x) if (x is not None) else '' # components formatting functions -bcomp_fn = lambda x: ','.join(to_str(x)) if (x is not None) else '' -pcomp_fn = lambda x, sep=',': subscripts(x,sep=sep) if (x is not None) else '' -vcomp_fn = lambda x: '_'+'_'.join(to_str(x)) if (x is not None) else '' -lcomp_fn = lambda x: '_<LBRACKET>{}<RBRACKET>'.format(','.join(to_str(x))) if (x is not None) else '' + + +def bcomp_fn(x): return ','.join(to_str(x)) if (x is not None) else '' + + +pcomp_fn = lambda x, sep=',': subscripts(x, sep=sep) if (x is not None) else '' + + +def vcomp_fn(x): return '_'+'_'.join(to_str(x)) if (x is not None) else '' + + +def lcomp_fn(x): return '_<LBRACKET>{}<RBRACKET>'.format(','.join(to_str(x))) if (x is not None) else '' # join formatting functions -bjoin_fn = lambda x: '_'.join(to_str(x)) if (x is not None) else '' -pjoin_fn = lambda x: ''.join(to_str(x)) if (x is not None) else '' -vjoin_fn = lambda x: '_'.join(to_str(x)) if (x is not None) else '' -ljoin_fn = lambda x: ''.join(to_str(x)) if (x is not None) else '' + + +def bjoin_fn(x): return '_'.join(to_str(x)) if (x is not None) else '' + + +def pjoin_fn(x): return ''.join(to_str(x)) if (x is not None) else '' + + +def vjoin_fn(x): return '_'.join(to_str(x)) if (x is not None) else '' + + +def ljoin_fn(x): return ''.join(to_str(x)) if (x is not None) else '' + # divide formatting functions -bdivide_fn = lambda x,y: '{}/{}'.format(x,y) -pdivide_fn = lambda x,y: '{}/{}'.format(*to_str(x,y)) -vdivide_fn = lambda x,y: '{}__{}'.format(x,y) -ldivide_fn = lambda x,y: r'\dfrac<LBRACKET>{}<RBRACKET><LBRACKET>{}<RBRACKET>'.format(x,y) +def bdivide_fn(x, y): return '{}/{}'.format(x, y) + + +def pdivide_fn(x, y): return '{}/{}'.format(*to_str(x, y)) + + +def vdivide_fn(x, y): return '{}__{}'.format(x, y) + + +def ldivide_fn(x, y): return r'\dfrac<LBRACKET>{}<RBRACKET><LBRACKET>{}<RBRACKET>'.format(x, y) class DifferentialStringFormatter(object): @@ -207,11 +276,11 @@ class DifferentialStringFormatter(object): See __main__ at the bottom of this file for usage. """ - exp_fns = (bexp_fn, pexp_fn, vexp_fn, lexp_fn) - pow_fns = (bpow_fn, ppow_fn, vpow_fn, lpow_fn) - sub_fns = (bsub_fn, psub_fn, vsub_fn, lsub_fn) - comp_fns = (bcomp_fn, pcomp_fn, vcomp_fn, lcomp_fn) - join_fns = (bjoin_fn, pjoin_fn, vjoin_fn, ljoin_fn) + exp_fns = (bexp_fn, pexp_fn, vexp_fn, lexp_fn) + pow_fns = (bpow_fn, ppow_fn, vpow_fn, lpow_fn) + sub_fns = (bsub_fn, psub_fn, vsub_fn, lsub_fn) + comp_fns = (bcomp_fn, pcomp_fn, vcomp_fn, lcomp_fn) + join_fns = (bjoin_fn, pjoin_fn, vjoin_fn, ljoin_fn) divide_fns = (bdivide_fn, pdivide_fn, vdivide_fn, ldivide_fn) @staticmethod @@ -220,16 +289,16 @@ class DifferentialStringFormatter(object): '<LBRACKET>': '{', '<RBRACKET>': '}', } - for (k,v) in special_characters.items(): - ss = ss.replace(k,v) + for (k, v) in special_characters.items(): + ss = ss.replace(k, v) return ss @classmethod def return_names(cls, *args, **kwds): # fsc = format special characters - fsc=kwds.get('fsc', True) - assert len(args)>=1 - if len(args)==1: + fsc = kwds.get('fsc', True) + assert len(args) >= 1 + if len(args) == 1: if fsc: return args[0] else: @@ -250,10 +319,10 @@ class DifferentialStringFormatter(object): dpow=1, varpow=1, components=None, trigp=3, fsc=True): assert (varpow != 0) - bd = '' if (dpow==0) else bd - pd = '' if (dpow==0) else pd - vd = '' if (dpow==0) else vd - ld = '' if (dpow==0) else ld + bd = '' if (dpow == 0) else bd + pd = '' if (dpow == 0) else pd + vd = '' if (dpow == 0) else vd + ld = '' if (dpow == 0) else ld blp = '' if len(bvar) <= trigp else blp brp = '' if len(bvar) <= trigp else brp plp = '' if len(pvar) <= trigp else plp @@ -262,7 +331,7 @@ class DifferentialStringFormatter(object): vrp = '' if len(vvar) <= trigp else vrp llp = '' if len(lvar) <= trigp else llp lrp = '' if len(lvar) <= trigp else lrp - template='{d}{dpow}{lp}{var}{components}{rp}{varpow}' + template = '{d}{dpow}{lp}{var}{components}{rp}{varpow}' bname = template.format(d=bd, dpow=bpow_fn(dpow), components=bcomp_fn(components), var=bvar, varpow=bpow_fn(varpow), @@ -283,28 +352,28 @@ class DifferentialStringFormatter(object): @classmethod def format_partial_names(cls, bvars, pvars, vvars, lvars, varpows, - bjoin_fn=bjoin_fn, pjoin_fn=pjoin_fn, vjoin_fn=vjoin_fn, ljoin_fn=ljoin_fn, - components=None, fsc=True, **kwds): + bjoin_fn=bjoin_fn, pjoin_fn=pjoin_fn, vjoin_fn=vjoin_fn, ljoin_fn=ljoin_fn, + components=None, fsc=True, **kwds): bvars, pvars, vvars, lvars = to_tuple(bvars), to_tuple(pvars), to_tuple(vvars), to_tuple(lvars) varpows = to_tuple(varpows) - assert len(bvars)==len(pvars)==len(vvars)==len(lvars)==len(varpows) - assert any(v>0 for v in varpows) + assert len(bvars) == len(pvars) == len(vvars) == len(lvars) == len(varpows) + assert any(v > 0 for v in varpows) nvars = len(bvars) if (components is not None): components = to_tuple(components) - assert len(components)==nvars + assert len(components) == nvars else: components = (None,)*nvars bnames, pnames, vnames, lnames = (), (), (), () for (bvar, pvar, vvar, lvar, varpow, component) in \ zip(bvars, pvars, vvars, lvars, varpows, components): - if (varpow==0): + if (varpow == 0): continue res = cls.format_partial_name(bvar=bvar, pvar=pvar, vvar=vvar, lvar=lvar, varpow=varpow, components=component, fsc=False, **kwds) - assert len(res)==4 + assert len(res) == 4 bnames += (res[0],) pnames += (res[1],) vnames += (res[2],) @@ -314,18 +383,18 @@ class DifferentialStringFormatter(object): @classmethod def format_pd(cls, bvar, pvar, vvar, lvar, - bxvars='x', pxvars=xsymbol, vxvars='x', lxvars='x', - varpows=1, var_components=None, xvars_components=None, - bdivide_fn=bdivide_fn, pdivide_fn=pdivide_fn, vdivide_fn=vdivide_fn, ldivide_fn=ldivide_fn, - fsc=True, **kwds): + bxvars='x', pxvars=xsymbol, vxvars='x', lxvars='x', + varpows=1, var_components=None, xvars_components=None, + bdivide_fn=bdivide_fn, pdivide_fn=pdivide_fn, vdivide_fn=vdivide_fn, ldivide_fn=ldivide_fn, + fsc=True, **kwds): for k in ('dpow', 'components', 'bvars', 'pvars', 'vvars', 'lvars', 'varpow'): assert k not in kwds, 'Cannot specify reserved keyword {}.'.format(k) bxvars, pxvars, vxvars, lxvars = to_tuple(bxvars), to_tuple(pxvars), to_tuple(vxvars), to_tuple(lxvars) varpows = to_tuple(varpows) - assert len(bxvars)==len(pxvars)==len(vxvars)==len(lxvars)==len(varpows) - assert any(v>0 for v in varpows) + assert len(bxvars) == len(pxvars) == len(vxvars) == len(lxvars) == len(varpows) + assert any(v > 0 for v in varpows) dpow = sum(varpows) numerator = cls.format_partial_name(bvar=bvar, pvar=pvar, @@ -349,7 +418,7 @@ class DifferentialStringFormatter(object): if __name__ == '__main__': def _print(*args, **kwds): if isinstance(args[0], tuple): - assert len(args)==1 + assert len(args) == 1 args = args[0] if ('multiline' in kwds) and (kwds['multiline'] is True): for a in args: @@ -366,55 +435,54 @@ if __name__ == '__main__': _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, dpow=1)) _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, dpow=2)) _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, dpow=3, components=0)) - _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, dpow=4, components=(0,2))) + _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, dpow=4, components=(0, 2))) print bvar, pvar, vvar, lvar = ('x',)*4 _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, varpow=1)) _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, varpow=2)) _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, varpow=3, components=0)) - _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, varpow=4, components=(0,2))) + _print(DifferentialStringFormatter.format_partial_name(bvar, pvar, vvar, lvar, varpow=4, components=(0, 2))) print - bvar, pvar, vvar, lvar = (('x','y'),)*4 + bvar, pvar, vvar, lvar = (('x', 'y'),)*4 try: - _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(0,0))) + _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(0, 0))) raise RuntimeError() except AssertionError: pass - _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(0,1))) - _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(1,0))) - _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(1,1))) - _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(1,2))) - _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(2,2))) - _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(2,2), components=(0,1))) - _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(2,2), components=((0,1),(1,0)))) + _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(0, 1))) + _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(1, 0))) + _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(1, 1))) + _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(1, 2))) + _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(2, 2))) + _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(2, 2), components=(0, 1))) + _print(DifferentialStringFormatter.format_partial_names(bvar, pvar, vvar, lvar, varpows=(2, 2), components=((0, 1), (1, 0)))) print bvar, pvar, vvar, lvar = 'Fext', 'Fₑₓₜ', 'Fext', '<LBRACKET>F_<LBRACKET>ext<RBRACKET><RBRACKET>' - bxvars, pxvars, vxvars, lxvars = (('x','y'),)*4 + bxvars, pxvars, vxvars, lxvars = (('x', 'y'),)*4 _print(DifferentialStringFormatter.format_pd(bvar, pvar, vvar, lvar)) _print(DifferentialStringFormatter.format_pd(bvar, pvar, vvar, lvar, varpows=2)) _print(DifferentialStringFormatter.format_pd(bvar, pvar, vvar, lvar, - bxvars, pxvars, vxvars, lxvars, - varpows=(1,0))) + bxvars, pxvars, vxvars, lxvars, + varpows=(1, 0))) _print(DifferentialStringFormatter.format_pd(bvar, pvar, vvar, lvar, - bxvars, pxvars, vxvars, lxvars, - varpows=(0,1))) + bxvars, pxvars, vxvars, lxvars, + varpows=(0, 1))) _print(DifferentialStringFormatter.format_pd(bvar, pvar, vvar, lvar, - bxvars, pxvars, vxvars, lxvars, - varpows=(1,1))) + bxvars, pxvars, vxvars, lxvars, + varpows=(1, 1))) _print(DifferentialStringFormatter.format_pd(bvar, pvar, vvar, lvar, - bxvars, pxvars, vxvars, lxvars, - varpows=(5,2))) + bxvars, pxvars, vxvars, lxvars, + varpows=(5, 2))) print bxvars, pxvars, vxvars, lxvars = (('x',)*5,)*4 varpows = (1,)*5 xvars_components = tuple(range(5)) - var_components=(0,4,3,2) + var_components = (0, 4, 3, 2) _print(DifferentialStringFormatter.format_pd(bvar, pvar, vvar, lvar, - bxvars, pxvars, vxvars, lxvars, - varpows=varpows, xvars_components=xvars_components, - var_components=var_components), multiline=True) - + bxvars, pxvars, vxvars, lxvars, + varpows=varpows, xvars_components=xvars_components, + var_components=var_components), multiline=True) diff --git a/hysop/tools/numerics.py b/hysop/tools/numerics.py index bcf7f4dbaac2b655d6afd2ab6c439b37dc76b242..db6c2abc3f2ac4682afcffcfe63a6984a9a1396e 100644 --- a/hysop/tools/numerics.py +++ b/hysop/tools/numerics.py @@ -1,30 +1,34 @@ import numpy as np import gmpy2 -from gmpy2 import mpq,mpz,mpfr,f2q +from gmpy2 import mpq, mpz, mpfr, f2q from hysop.constants import HYSOP_REAL, HYSOP_INTEGER, HYSOP_INDEX, HYSOP_BOOL, HYSOP_COMPLEX -MPQ = mpq(0).__class__ -MPZ = mpz(0).__class__ -MPFR = mpfr(0).__class__ -F2Q = f2q(0).__class__ +MPQ = mpq(0).__class__ +MPZ = mpz(0).__class__ +MPFR = mpfr(0).__class__ +F2Q = f2q(0).__class__ + def _mpqize(x): if isinstance(x, int): - return mpq(x,1) + return mpq(x, 1) elif isinstance(x, float): return f2q(x) else: return mpq(str(x)) + + mpqize = np.vectorize(_mpqize) + def get_dtype(x): if isinstance(x, np.dtype): return x.type elif hasattr(x, 'dtype'): if callable(x.dtype): return x.dtype() - elif x.dtype.__class__.__name__ == 'getset_descriptor': # dtype.type has a dtype field... + elif x.dtype.__class__.__name__ == 'getset_descriptor': # dtype.type has a dtype field... return x else: return x.dtype @@ -37,33 +41,40 @@ def get_dtype(x): elif (x is None): return None else: - msg='Unknown type in get_dtype (got {}).' - msg=msg.format(x.__class__) + msg = 'Unknown type in get_dtype (got {}).' + msg = msg.format(x.__class__) raise TypeError(msg) + def get_itemsize(x): dtype = np.dtype(get_dtype(x)) return dtype.itemsize + def is_fp(x): - types=(np.float16, np.float32, np.float64, np.longdouble) + types = (np.float16, np.float32, np.float64, np.longdouble) return (get_dtype(x) in types) + def is_signed(x): types = (np.int8, np.int16, np.int32, np.int64) return (get_dtype(x) in types) + def is_unsigned(x): - types = (np.bool, np.uint8, np.uint16, np.uint32, np.uint64) + types = (np.bool_, np.uint8, np.uint16, np.uint32, np.uint64) return (get_dtype(x) in types) + def is_integer(x): return is_signed(x) or is_unsigned(x) + def is_complex(x): types = (np.complex64, np.complex128, np.clongdouble) return (get_dtype(x) in types) + def default_invalid_value(dtype): nan = float('nan') if is_complex(dtype): @@ -78,19 +89,21 @@ def default_invalid_value(dtype): raise NotImplementedError # promote_dtype + + def match_dtype(x, dtype): """Promote x.dtype to dtype (always safe cast).""" xtype = get_dtype(x) if isinstance(dtype, str): - if dtype=='f': + if dtype == 'f': return np.promote_types(xtype, np.float16) - elif dtype=='i': + elif dtype == 'i': return np.promote_types(xtype, np.int8) - elif dtype=='u': + elif dtype == 'u': return np.promote_types(xtype, np.uint8) - elif dtype=='b': + elif dtype == 'b': return np.promote_types(xtype, HYSOP_BOOL) - elif dtype=='c': + elif dtype == 'c': return np.promote_types(xtype, np.complex64) else: raise NotImplementedError(dtype) @@ -101,6 +114,7 @@ def match_dtype(x, dtype): else: return dtype + def demote_dtype(x, dtype): """Demote x.dtype to dtype (not a safe cast).""" xtype = get_dtype(x) @@ -108,14 +122,14 @@ def demote_dtype(x, dtype): if is_complex(xtype): n //= 2 if isinstance(dtype, str): - if dtype=='c': + if dtype == 'c': return {1: np.complex64, 2: np.complex64, 4: np.complex64, 8: np.complex128, 16: np.clongdouble}[n] - elif dtype=='f': + elif dtype == 'f': return {1: np.float16, 2: np.float16, 4: np.float32, 8: np.float64, 16: np.longdouble}[n] - elif dtype=='i': - return {1: np.int8, 2: np.int16, 4:np.int32, 8: np.int64}[n] - elif dtype=='u': - return {1: np.uint8, 2: np.uint16, 4:np.uint32, 8: np.uint64}[n] + elif dtype == 'i': + return {1: np.int8, 2: np.int16, 4: np.int32, 8: np.int64}[n] + elif dtype == 'u': + return {1: np.uint8, 2: np.uint16, 4: np.uint32, 8: np.uint64}[n] else: raise NotImplementedError(dtype) elif (xtype is None): @@ -125,20 +139,26 @@ def demote_dtype(x, dtype): else: return dtype + def match_float_type(x): - return match_dtype(x,'f') + return match_dtype(x, 'f') + def match_signed_type(x): - return match_dtype(x,'i') + return match_dtype(x, 'i') + def match_unsigned_type(x): - return match_dtype(x,'i') + return match_dtype(x, 'i') + def match_complex_type(x): - return match_dtype(x,'c') + return match_dtype(x, 'c') + def match_bool_type(x): - return match_dtype(x,'b') + return match_dtype(x, 'b') + def complex_to_float_dtype(dtype): dtype = get_dtype(dtype) @@ -150,24 +170,26 @@ def complex_to_float_dtype(dtype): elif dtype == np.clongdouble: return np.longdouble else: - msg=msg.format(dtype) - msg='Unknown complex type {}.' + msg = msg.format(dtype) + msg = 'Unknown complex type {}.' raise RuntimeError(msg) + def float_to_complex_dtype(dtype): dtype = get_dtype(dtype) assert is_fp(dtype), '{} is not a float'.format(dtype) - if dtype==np.float32: + if dtype == np.float32: return np.complex64 - elif dtype==np.float64: + elif dtype == np.float64: return np.complex128 - elif dtype==np.longdouble: + elif dtype == np.longdouble: return np.clongdouble else: - msg='Unknown float type {}.' - msg=msg.format(dtype) + msg = 'Unknown float type {}.' + msg = msg.format(dtype) raise RuntimeError(msg) + def determine_fp_types(dtype): if is_fp(dtype): ftype = dtype @@ -176,23 +198,24 @@ def determine_fp_types(dtype): ctype = dtype ftype = complex_to_float_dtype(ctype) else: - msg='{} is not a floating point or complex data type.' - msg=msg.format(dtype) + msg = '{} is not a floating point or complex data type.' + msg = msg.format(dtype) raise ValueError(msg) return (np.dtype(ftype), np.dtype(ctype)) + def find_common_dtype(*args): - dtypes = tuple(get_dtype(arg) for arg in args) + dtypes = tuple(get_dtype(arg) for arg in args) itemsize = tuple(get_itemsize(x) for x in dtypes) - n = max(itemsize) + n = max(itemsize) if any(is_complex(x) for x in dtypes): return {8: np.complex64, 16: np.complex128, 32: np.clongdouble}[n] elif any(is_fp(x) for x in dtypes): return {2: np.float16, 4: np.float32, 8: np.float64, 16: np.longdouble}[n] elif any(is_signed(x) for x in dtypes): - return {1: np.int8, 2: np.int16, 4:np.int32, 8: np.int64}[n] + return {1: np.int8, 2: np.int16, 4: np.int32, 8: np.int64}[n] elif any(is_unsigned(x) for x in dtypes): - return {1: np.uint8, 2: np.uint16, 4:np.uint32, 8: np.uint64}[n] + return {1: np.uint8, 2: np.uint16, 4: np.uint32, 8: np.uint64}[n] else: - msg='Did not find any matching dtype.' + msg = 'Did not find any matching dtype.' raise NotImplementedError(msg)