def dspace_type(space, impl, dtype=None): """Select the correct corresponding n-tuples space. Parameters ---------- space : `LinearSpace` Template space from which to infer an adequate data space. If it has a `LinearSpace.field` attribute, ``dtype`` must be consistent with it. impl : string Implementation backend for the data space dtype : `numpy.dtype`, optional Data type which the space is supposed to use. If ``None`` is given, the space type is purely determined from ``space`` and ``impl``. Otherwise, it must be compatible with the field of ``space``. Returns ------- stype : type Space type selected after the space's field, the backend and the data type """ spacetype_map = {RealNumbers: FN_IMPLS, ComplexNumbers: FN_IMPLS, type(None): NTUPLES_IMPLS} field_type = type(getattr(space, 'field', None)) if dtype is None: pass elif is_real_floating_dtype(dtype): if field_type is None or field_type == ComplexNumbers: raise TypeError('real floating data type {!r} requires space ' 'field to be of type RealNumbers, got {}' ''.format(dtype, field_type)) elif is_complex_floating_dtype(dtype): if field_type is None or field_type == RealNumbers: raise TypeError('complex floating data type {!r} requires space ' 'field to be of type ComplexNumbers, got {!r}' ''.format(dtype, field_type)) elif is_scalar_dtype(dtype): if field_type == ComplexNumbers: raise TypeError('non-floating data type {!r} requires space field ' 'to be of type RealNumbers, got {!r}' .format(dtype, field_type)) else: raise TypeError('non-scalar data type {!r} cannot be combined with ' 'a `LinearSpace`'.format(dtype)) stype = spacetype_map[field_type].get(impl, None) if stype is None: raise NotImplementedError('no corresponding data space available ' 'for space {!r} and implementation {!r}' ''.format(space, impl)) return stype
def dspace_type(space, impl, dtype=None): """Select the correct corresponding n-tuples space. Parameters ---------- space : `object` The template space. If it has a ``field`` attribute, ``dtype`` must be consistent with it impl : {'numpy', 'cuda'} The backend for the data space dtype : `type`, optional Data type which the space is supposed to use. If `None`, the space type is purely determined from ``space`` and ``impl``. If given, it must be compatible with the field of ``space``. Non-floating types result in basic `Fn`-type spaces. Returns ------- stype : `type` Space type selected after the space's field, the backend and the data type """ impl_ = str(impl).lower() if impl_ not in ('numpy', 'cuda'): raise ValueError('implementation type {} not understood.' ''.format(impl)) if impl_ == 'cuda' and not CUDA_AVAILABLE: raise ValueError('CUDA implementation not available.') basic_map = {'numpy': Fn, 'cuda': CudaFn} spacetype_map = { 'numpy': {RealNumbers: Rn, ComplexNumbers: Cn, type(None): Ntuples}, 'cuda': {RealNumbers: CudaRn, ComplexNumbers: None, type(None): CudaNtuples} } field_type = type(getattr(space, 'field', None)) if dtype is None: stype = spacetype_map[impl_][field_type] elif is_real_floating_dtype(dtype): if field_type is None or field_type == ComplexNumbers: raise TypeError('real floating data type {!r} requires space ' 'field to be of type `RealNumbers`, got {}.' ''.format(dtype, field_type)) stype = spacetype_map[impl_][field_type] elif is_complex_floating_dtype(dtype): if field_type is None or field_type == RealNumbers: raise TypeError('complex floating data type {!r} requires space ' 'field to be of type `ComplexNumbers`, got {!r}.' ''.format(dtype, field_type)) stype = spacetype_map[impl_][field_type] elif is_scalar_dtype(dtype): if field_type == ComplexNumbers: raise TypeError('non-floating data type {!r} requires space field ' 'to be of type `RealNumbers`, got {!r}.' .format(dtype, field_type)) elif field_type == RealNumbers: stype = basic_map[impl_] else: stype = spacetype_map[impl_][field_type] elif field_type is None: # Only in this case are arbitrary types allowed stype = spacetype_map[impl_][field_type] else: raise TypeError('non-scalar data type {!r} cannot be combined with ' 'a `LinearSpace`.'.format(dtype)) if stype is None: raise NotImplementedError('no corresponding data space available ' 'for space {!r} and implementation {!r}.' ''.format(space, impl)) return stype
def dspace_type(space, impl, dtype=None): """Select the correct corresponding n-tuples space. Parameters ---------- space : Template space from which to infer an adequate data space. If it has a `LinearSpace.field` attribute, ``dtype`` must be consistent with it. impl : {'numpy', 'cuda'} Implementation backend for the data space dtype : `type`, optional Data type which the space is supposed to use. If `None`, the space type is purely determined from ``space`` and ``impl``. If given, it must be compatible with the field of ``space``. Returns ------- stype : `type` Space type selected after the space's field, the backend and the data type """ impl, impl_in = str(impl).lower(), impl if impl not in ('numpy', 'cuda'): raise ValueError("`impl` '{}' not understood" ''.format(impl_in)) if impl == 'cuda' and not CUDA_AVAILABLE: raise ValueError("'cuda' implementation not available") basic_map = {'numpy': Fn, 'cuda': CudaFn} spacetype_map = { 'numpy': {RealNumbers: Fn, ComplexNumbers: Fn, type(None): Ntuples}, 'cuda': {RealNumbers: CudaFn, ComplexNumbers: None, type(None): CudaNtuples} } field_type = type(getattr(space, 'field', None)) if dtype is None: stype = spacetype_map[impl][field_type] elif is_real_floating_dtype(dtype): if field_type is None or field_type == ComplexNumbers: raise TypeError('real floating data type {!r} requires space ' 'field to be of type RealNumbers, got {}' ''.format(dtype, field_type)) stype = spacetype_map[impl][field_type] elif is_complex_floating_dtype(dtype): if field_type is None or field_type == RealNumbers: raise TypeError('complex floating data type {!r} requires space ' 'field to be of type ComplexNumbers, got {!r}' ''.format(dtype, field_type)) stype = spacetype_map[impl][field_type] elif is_scalar_dtype(dtype): if field_type == ComplexNumbers: raise TypeError('non-floating data type {!r} requires space field ' 'to be of type RealNumbers, got {!r}' .format(dtype, field_type)) elif field_type == RealNumbers: stype = basic_map[impl] else: stype = spacetype_map[impl][field_type] elif field_type is None: # Only in this case are arbitrary types allowed stype = spacetype_map[impl][field_type] else: raise TypeError('non-scalar data type {!r} cannot be combined with ' 'a `LinearSpace`'.format(dtype)) if stype is None: raise NotImplementedError('no corresponding data space available ' 'for space {!r} and implementation {!r}' ''.format(space, impl)) return stype
def test_is_complex_floating_dtype(): for dtype in complex_float_dtypes: assert is_complex_floating_dtype(dtype)
def test_is_complex_floating_dtype(): for dtype in complex_float_dtypes: assert is_complex_floating_dtype(dtype)
def vector(array, dtype=None, impl="numpy"): """Create an n-tuples type vector from an array. Parameters ---------- array : array-like Array from which to create the vector. Scalars become one-dimensional vectors. dtype : `object`, optional Set the data type of the vector manually with this option. By default, the space type is inferred from the input data. impl : {'numpy', 'cuda'} Implementation backend for the vector Returns ------- vec : `NtuplesBaseVector` Vector created from the input array. Its concrete type depends on the provided arguments. Notes ----- This is a convenience function and not intended for use in speed-critical algorithms. It creates a NumPy array first, hence especially CUDA vectors as input result in a large speed penalty. Examples -------- >>> vector([1, 2, 3]) # No automatic cast to float Fn(3, 'int').element([1, 2, 3]) >>> vector([1, 2, 3], dtype=float) Rn(3).element([1.0, 2.0, 3.0]) >>> vector([1 + 1j, 2, 3 - 2j]) Cn(3).element([(1+1j), (2+0j), (3-2j)]) Non-scalar types are also supported: >>> vector([u'Hello,', u' world!']) Ntuples(2, '<U7').element([u'Hello,', u' world!']) Scalars become a one-element vector: >>> vector(0.0) Rn(1).element([0.0]) """ # Sanitize input arr = np.array(array, copy=False, ndmin=1) impl = str(impl).lower() # Validate input if arr.ndim > 1: raise ValueError("array has {} dimensions, expected 1." "".format(arr.ndim)) # Set dtype if dtype is not None: space_dtype = dtype elif arr.dtype == float and impl == "cuda": # Special case, default float is float32 on cuda space_dtype = "float32" else: space_dtype = arr.dtype # Select implementation if impl == "numpy": if is_real_floating_dtype(space_dtype): space_type = Rn elif is_complex_floating_dtype(space_dtype): space_type = Cn elif is_scalar_dtype(space_dtype): space_type = Fn else: space_type = Ntuples elif impl == "cuda": if not CUDA_AVAILABLE: raise ValueError("CUDA implementation not available.") if is_real_floating_dtype(space_dtype): space_type = CudaRn elif is_complex_floating_dtype(space_dtype): raise NotImplementedError("complex spaces in CUDA not supported.") elif is_scalar_dtype(space_dtype): space_type = CudaFn else: space_type = CudaNtuples else: raise ValueError("implementation '{}' not understood.".format(impl)) return space_type(len(arr), dtype=space_dtype).element(arr)
def __init__(self, domain, field=None, out_dtype=None): """Initialize a new instance. Parameters ---------- domain : `Set` The domain of the functions field : `Field`, optional The range of the functions, usually the `RealNumbers` or `ComplexNumbers`. If not given, the field is either inferred from ``out_dtype``, or, if the latter is also `None`, set to ``RealNumbers()``. out_dtype : optional Data type of the return value of a function in this space. Can be given in any way `numpy.dtype` understands, e.g. as string ('float64') or data type (`float`). By default, 'float64' is used for real and 'complex128' for complex spaces. """ if not isinstance(domain, Set): raise TypeError('`domain` {!r} not a Set instance'.format(domain)) if field is not None and not isinstance(field, Field): raise TypeError('`field` {!r} not a `Field` instance' ''.format(field)) # Data type: check if consistent with field, take default for None dtype, dtype_in = np.dtype(out_dtype), out_dtype # Default for both None if field is None and out_dtype is None: field = RealNumbers() out_dtype = np.dtype('float64') # field None, dtype given -> infer field elif field is None: if is_real_dtype(dtype): field = RealNumbers() elif is_complex_floating_dtype(dtype): field = ComplexNumbers() else: raise ValueError('{} is not a scalar data type' ''.format(dtype_in)) # field given -> infer dtype if not given, else check consistency elif field == RealNumbers(): if out_dtype is None: out_dtype = np.dtype('float64') elif not is_real_dtype(dtype): raise ValueError('{} is not a real data type' ''.format(dtype_in)) elif field == ComplexNumbers(): if out_dtype is None: out_dtype = np.dtype('complex128') elif not is_complex_floating_dtype(dtype): raise ValueError('{} is not a complex data type' ''.format(dtype_in)) # Else: keep out_dtype=None, which results in lazy dtype determination LinearSpace.__init__(self, field) FunctionSet.__init__(self, domain, field, out_dtype) # Init cache attributes for real / complex variants if self.field == RealNumbers(): self._real_out_dtype = self.out_dtype self._real_space = self self._complex_out_dtype = _TYPE_MAP_R2C.get(self.out_dtype, np.dtype(object)) self._complex_space = None elif self.field == ComplexNumbers(): self._real_out_dtype = _TYPE_MAP_C2R[self.out_dtype] self._real_space = None self._complex_out_dtype = self.out_dtype self._complex_space = self else: self._real_out_dtype = None self._real_space = None self._complex_out_dtype = None self._complex_space = None
def uniform_discr_fromspace(fspace, nsamples, exponent=2.0, interp='nearest', impl='numpy', **kwargs): """Discretize an Lp function space by uniform partition. Parameters ---------- fspace : `FunctionSpace` Continuous function space. Its domain must be an `IntervalProd` instance. nsamples : `int` or `tuple` of `int` Number of samples per axis. For dimension >= 2, a tuple is required. exponent : positive `float`, optional The parameter ``p`` in ``L^p``. If the exponent is not equal to the default 2.0, the space has no inner product. interp : `str` or `sequence` of `str`, optional Interpolation type to be used for discretization. A sequence is interpreted as interpolation scheme per axis. 'nearest' : use nearest-neighbor interpolation 'linear' : use linear interpolation impl : {'numpy', 'cuda'}, optional Implementation of the data storage arrays Other Parameters ---------------- nodes_on_bdry : `bool` or boolean `array-like`, optional If `True`, place the outermost grid points at the boundary. For `False`, they are shifted by half a cell size to the 'inner'. If an array-like is given, it must have shape ``(ndim, 2)``, where ``ndim`` is the number of dimensions. It defines per axis whether the leftmost (first column) and rightmost (second column) nodes node lie on the boundary. Default: `False` order : {'C', 'F'}, optional Axis ordering in the data storage. Default: 'C' dtype : dtype, optional Data type for the discretized space. If not specified, the `FunctionSpace.out_dtype` of ``fspace`` is used. weighting : {'const', 'none'}, optional Weighting of the discretized space functions. 'const' : weight is a constant, the cell volume (default) 'none' : no weighting Returns ------- discr : `DiscreteLp` The uniformly discretized function space Examples -------- >>> from odl import Interval, FunctionSpace >>> intv = Interval(0, 1) >>> space = FunctionSpace(intv) >>> uniform_discr_fromspace(space, 10) uniform_discr(0.0, 1.0, 10) See also -------- uniform_discr : implicit uniform Lp discretization uniform_discr_frompartition : uniform Lp discretization using a given uniform partition of a function domain uniform_discr_fromintv : uniform discretization from an existing interval product odl.discr.partition.uniform_partition : partition of the function domain """ if not isinstance(fspace, FunctionSpace): raise TypeError('space {!r} is not a `FunctionSpace` instance.' ''.format(fspace)) if not isinstance(fspace.domain, IntervalProd): raise TypeError('domain {!r} of the function space is not an ' '`IntervalProd` instance.'.format(fspace.domain)) impl, impl_in = str(impl).lower(), impl dtype = kwargs.pop('dtype', None) # Set data type. If given check consistency with fspace's field and # out_dtype. If not given, take the latter. if dtype is None: dtype = fspace.out_dtype else: dtype, dtype_in = np.dtype(dtype), dtype if not np.can_cast(fspace.out_dtype, dtype, casting='safe'): raise ValueError('cannot safely cast from output data {} type of ' 'the function space to given data type {}.' ''.format(fspace.out, dtype_in)) if fspace.field == RealNumbers() and not is_real_dtype(dtype): raise ValueError('cannot discretize real space {} with ' 'non-real data type {}.' ''.format(fspace, dtype)) elif (fspace.field == ComplexNumbers() and not is_complex_floating_dtype(dtype)): raise ValueError('cannot discretize complex space {} with ' 'non-complex-floating data type {}.' ''.format(fspace, dtype)) nodes_on_bdry = kwargs.pop('nodes_on_bdry', False) partition = uniform_partition_fromintv(fspace.domain, nsamples, nodes_on_bdry) return uniform_discr_frompartition(partition, exponent, interp, impl_in, dtype=dtype, **kwargs)
def vector(array, dtype=None, impl='numpy'): """Create an n-tuples type vector from an array. Parameters ---------- array : `array-like` Array from which to create the vector. Scalars become one-dimensional vectors. dtype : `object`, optional Set the data type of the vector manually with this option. By default, the space type is inferred from the input data. impl : {'numpy', 'cuda'} Implementation backend for the vector Returns ------- vec : `NtuplesBaseVector` Vector created from the input array. Its concrete type depends on the provided arguments. Notes ----- This is a convenience function and not intended for use in speed-critical algorithms. It creates a NumPy array first, hence especially CUDA vectors as input result in a large speed penalty. Examples -------- >>> vector([1, 2, 3]) # No automatic cast to float Fn(3, 'int').element([1, 2, 3]) >>> vector([1, 2, 3], dtype=float) Rn(3).element([1.0, 2.0, 3.0]) >>> vector([1 + 1j, 2, 3 - 2j]) Cn(3).element([(1+1j), (2+0j), (3-2j)]) Non-scalar types are also supported: >>> vector([True, False]) Ntuples(2, 'bool').element([True, False]) Scalars become a one-element vector: >>> vector(0.0) Rn(1).element([0.0]) """ # Sanitize input arr = np.array(array, copy=False, ndmin=1) impl, impl_in = str(impl).lower(), impl # Validate input if arr.ndim > 1: raise ValueError('array has {} dimensions, expected 1' ''.format(arr.ndim)) # Set dtype if dtype is not None: space_dtype = dtype elif arr.dtype == np.dtype('float64') and impl == 'cuda': # Special case, default float is float32 on cuda space_dtype = 'float32' else: space_dtype = arr.dtype # Select implementation if impl == 'numpy': if is_real_floating_dtype(space_dtype): space_type = Rn elif is_complex_floating_dtype(space_dtype): space_type = Cn elif is_scalar_dtype(space_dtype): space_type = Fn else: space_type = Ntuples elif impl == 'cuda': if not CUDA_AVAILABLE: raise ValueError("'cuda' implementation not available") if is_real_floating_dtype(space_dtype): space_type = CudaRn elif is_complex_floating_dtype(space_dtype): raise NotImplementedError('complex spaces in CUDA not supported') elif is_scalar_dtype(space_dtype): space_type = CudaFn else: space_type = CudaNtuples else: raise ValueError("`impl` '{}' not understood".format(impl_in)) return space_type(len(arr), dtype=space_dtype).element(arr)