def __init__(self, shape, name='ODLTensorflowSpace'): super(TensorflowSpace, self).__init__(RealNumbers()) self.shape = tuple( tf.Dimension(si) if not isinstance(si, tf.Dimension) else si for si in shape) self.init_shape = tuple(si if si.value is not None else tf.Dimension(1) for si in self.shape) self.name = name
def _abs_pow_ufunc(self, fi, out): """Compute |F_i(x)|^p point-wise and write to ``out``.""" # Optimization for a very common case if self.exponent == 2.0 and self.base_space.field == RealNumbers(): fi.multiply(fi, out=out) else: fi.ufuncs.absolute(out=out) out.ufuncs.power(self.exponent, out=out)
def ufunc_factory(domain=RealNumbers()): # Create a `Operator` or `Functional` depending on arguments try: if isinstance(domain, Field): return globals()[name + '_func'](domain) else: return globals()[name + '_op'](domain) except KeyError: raise ValueError('ufunc not available for {}'.format(domain))
def __init__(self, space): """Initialize a new instance. Parameters ---------- space : `LinearSpace` Space to take the norm in. Examples -------- >>> r2 = odl.rn(2) >>> op = NormOperator(r2) >>> op([3, 4]) 5.0 """ super(NormOperator, self).__init__(space, RealNumbers(), linear=False)
def __init__(self, vector): """Initialize a new instance. Parameters ---------- vector : `LinearSpaceElement` Point to calculate the distance to. Examples -------- >>> r2 = odl.rn(2) >>> x = r2.element([1, 1]) >>> op = DistOperator(x) >>> op([4, 5]) 5.0 """ self.__vector = vector super().__init__(vector.space, RealNumbers(), linear=False)
def element(self, fcall=None, vectorized=True): """Create a `FunctionSpace` element. Parameters ---------- fcall : callable, optional The actual instruction for out-of-place evaluation. It must return a `FunctionSet.range` element or a `numpy.ndarray` of such (vectorized call). If fcall is a `FunctionSetElement`, it is wrapped as a new `FunctionSpaceElement`. vectorized : bool, optional Whether ``fcall`` supports vectorized evaluation. Returns ------- element : `FunctionSpaceElement` The new element, always supports vectorization Notes ----- If you specify ``vectorized=False``, the function is decorated with a vectorizer, which makes two elements created this way from the same function being regarded as *not equal*. """ if fcall is None: return self.zero() elif fcall in self: return fcall else: if not callable(fcall): raise TypeError('`fcall` {!r} is not callable'.format(fcall)) if not vectorized: if self.field == RealNumbers(): dtype = 'float64' else: dtype = 'complex128' fcall = vectorize(otypes=[dtype])(fcall) return self.element_type(self, fcall)
def __init__(self, size, dtype): """Initialize a new instance. Parameters ---------- size : non-negative int Number of entries in a tuple. dtype : Data type for each tuple entry. Can be provided in any way the `numpy.dtype` function understands, most notably as built-in type, as one of NumPy's internal datatype objects or as string. Only scalar data types (numbers) are allowed. """ NtuplesBase.__init__(self, size, dtype) if not is_scalar_dtype(self.dtype): raise TypeError('{!r} is not a scalar data type'.format(dtype)) if is_real_dtype(self.dtype): field = RealNumbers() self.__is_real = True self.__real_dtype = self.dtype self.__real_space = self try: self.__complex_dtype = complex_dtype(self.dtype) except ValueError: self.__complex_dtype = None self.__complex_space = None # Set in first call of astype else: field = ComplexNumbers() self.__is_real = False try: self.__real_dtype = real_dtype(self.dtype) except ValueError: self.__real_dtype = None self.__real_space = None # Set in first call of astype self.__complex_dtype = self.dtype self.__complex_space = self self.__is_floating = is_floating_dtype(self.dtype) LinearSpace.__init__(self, field)
def __init__(self, par_space, ft_kernel): """Initialize a new instance. Parameters ---------- par_space : `ProductSpace` Parameter space of the deformations, i.e. the space of the ``alpha_k`` parametrizing the deformations kernel_op : `numpy.ndarray` or `Operator` The kernel matrix defining the norm. If an operator is given, it is called to evaluate the matrix-vector product of the kernel matrix with a given ``alpha``. """ super().__init__(par_space, RealNumbers(), linear=False) # if isinstance(kernel_op, Operator): # self._kernel_op = kernel_op # else: # self._kernel_op = odl.MatVecOperator(kernel_op) self.par_space = par_space self.ft_kernel = ft_kernel
def rn(size, dtype=None, impl='numpy', **kwargs): """Return the real vector space ``R^n``. Parameters ---------- size : positive int The number of dimensions of the space dtype : `object`, optional The data type of the storage array. Can be provided in any way the `numpy.dtype` function understands, most notably as built-in type, as one of NumPy's internal datatype objects or as string. Only real floating-point data types are allowed. Default: default of the implementation given by calling ``default_dtype(RealNumbers())`` on the `FnBase` implementation. impl : string, optional The backend to use. See `odl.space.entry_points.FN_IMPLS` for available options. kwargs : Extra keyword arguments to pass to the implmentation. Returns ------- rn : `FnBase` See Also -------- fn : n-tuples over a field with arbitrary scalar data type. """ rn_impl = FN_IMPLS[impl] if dtype is None: dtype = rn_impl.default_dtype(RealNumbers()) rn = rn_impl(size, dtype, **kwargs) if not rn.is_rn: raise TypeError('data type {!r} not a real floating-point type.' ''.format(dtype)) return rn
def __str__(self): """Return ``str(self)``.""" inner_str = '{}'.format(self.domain) dtype_str = dtype_repr(self.out_dtype) if self.field == RealNumbers(): if self.out_dtype == np.dtype('float64'): pass else: inner_str += ', out_dtype={}'.format(dtype_str) elif self.field == ComplexNumbers(): if self.out_dtype == np.dtype('complex128'): inner_str += ', field={!r}'.format(self.field) else: inner_str += ', out_dtype={}'.format(dtype_str) else: # different field, name explicitly inner_str += ', field={!r}'.format(self.field) inner_str += ', out_dtype={}'.format(dtype_str) return '{}({})'.format(self.__class__.__name__, inner_str)
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 = complex_dtype(self.out_dtype, default=np.dtype(object)) self.__complex_space = None elif self.field == ComplexNumbers(): self.__real_out_dtype = real_dtype(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 ufunc_functional_factory(name, nargin, nargout, docstring): """Create a ufunc `Functional` from a given specification.""" assert 0 <= nargin <= 2 def __init__(self, field): """Initialize an instance. Parameters ---------- field : `Field` The domain of the functional. """ if not isinstance(field, Field): raise TypeError('`field` {!r} not a `Field`'.format(space)) if _is_integer_only_ufunc(name): raise ValueError("ufunc '{}' only defined with integral dtype" "".format(name)) linear = name in LINEAR_UFUNCS Functional.__init__(self, space=field, linear=linear) def _call(self, x): """Return ``self(x)``.""" if nargin == 1: return getattr(np, name)(x) else: return getattr(np, name)(*x) def __repr__(self): """Return ``repr(self)``.""" return '{}({!r})'.format(name, self.domain) # Create example (also functions as doctest) if nargin != 1: raise NotImplementedError('Currently not suppored') if nargout != 1: raise NotImplementedError('Currently not suppored') space = RealNumbers() val = 1.0 arg = '{}'.format(val) with np.errstate(all='ignore'): result = np.float64(getattr(np, name)(val)) examples_docstring = RAW_EXAMPLES_DOCSTRING.format(space=space, name=name, arg=arg, result=result) full_docstring = docstring + examples_docstring attributes = { "__init__": __init__, "_call": _call, "gradient": property(gradient_factory(name)), "__repr__": __repr__, "__doc__": full_docstring } full_name = name + '_op' return type(full_name, (Functional, ), attributes)
def reciprocal_space(space, axes=None, halfcomplex=False, shift=True, **kwargs): """Return the range of the Fourier transform on ``space``. Parameters ---------- space : `DiscreteLp` Real space whose reciprocal is calculated. It must be uniformly discretized. axes : sequence of ints, optional Dimensions along which the Fourier transform is taken. Default: all axes halfcomplex : bool, optional If ``True``, take only the negative frequency part along the last axis for. For ``False``, use the full frequency space. This option can only be used if ``space`` is a space of real-valued functions. shift : bool or sequence of bools, optional If ``True``, the reciprocal grid is shifted by half a stride in the negative direction. With a boolean sequence, this option is applied separately to each axis. If a sequence is provided, it must have the same length as ``axes`` if supplied. Note that this must be set to ``True`` in the halved axis in half-complex transforms. Default: ``True`` impl : string, optional Implementation back-end for the created space. Default: ``'numpy'`` exponent : float, optional Create a space with this exponent. By default, the conjugate exponent ``q = p / (p - 1)`` of the exponent of ``space`` is used, where ``q = inf`` for ``p = 1`` and vice versa. dtype : optional Complex data type of the created space. By default, the complex counterpart of ``space.dtype`` is used. Returns ------- rspace : `DiscreteLp` Reciprocal of the input ``space``. If ``halfcomplex=True``, the upper end of the domain (where the half space ends) is chosen to coincide with the grid node. """ if not isinstance(space, DiscreteLp): raise TypeError('`space` {!r} is not a `DiscreteLp` instance' ''.format(space)) if axes is None: axes = tuple(range(space.ndim)) axes = normalized_axes_tuple(axes, space.ndim) if not all(space.is_uniform_byaxis[axis] for axis in axes): raise ValueError('`space` is not uniformly discretized in the ' '`axes` of the transform') if halfcomplex and space.field != RealNumbers(): raise ValueError('`halfcomplex` option can only be used with real ' 'spaces') exponent = kwargs.pop('exponent', None) if exponent is None: exponent = conj_exponent(space.exponent) dtype = kwargs.pop('dtype', None) if dtype is None: dtype = complex_dtype(space.dtype) else: if not is_complex_floating_dtype(dtype): raise ValueError('{} is not a complex data type' ''.format(dtype_repr(dtype))) impl = kwargs.pop('impl', 'numpy') # Calculate range recip_grid = reciprocal_grid(space.grid, shift=shift, halfcomplex=halfcomplex, axes=axes) # Make a partition with nodes on the boundary in the last transform axis # if `halfcomplex == True`, otherwise a standard partition. if halfcomplex: max_pt = {axes[-1]: recip_grid.max_pt[axes[-1]]} part = uniform_partition_fromgrid(recip_grid, max_pt=max_pt) else: part = uniform_partition_fromgrid(recip_grid) # Use convention of adding a hat to represent fourier transform of variable axis_labels = list(space.axis_labels) for i in axes: # Avoid double math label = axis_labels[i].replace('$', '') axis_labels[i] = '$\^{{{}}}$'.format(label) recip_spc = uniform_discr_frompartition(part, exponent=exponent, dtype=dtype, impl=impl, axis_labels=axis_labels) return recip_spc
def rn(shape, dtype=None, impl='numpy', **kwargs): """Return a space of real tensors. Parameters ---------- shape : positive int or sequence of positive ints Number of entries per axis for elements in this space. A single integer results in a space with 1 axis. dtype : optional Data type of each element. Can be provided in any way the `numpy.dtype` function understands, e.g. as built-in type or as a string. Only real floating-point data types are allowed. For ``None``, the `TensorSpace.default_dtype` of the created space is used in the form ``default_dtype(RealNumbers())``. impl : str, optional Impmlementation back-end for the space. See `odl.space.entry_points.tensor_space_impl_names` for available options. kwargs : Extra keyword arguments passed to the space constructor. Returns ------- real_space : `TensorSpace` Examples -------- Space of real 3-tuples with ``float32`` entries: >>> odl.rn(3, dtype='float32') rn(3, dtype='float32') Real 2x3 tensors with ``float32`` entries: >>> odl.rn((2, 3), dtype='float32') rn((2, 3), dtype='float32') The default data type depends on the implementation. For ``impl='numpy'``, it is ``'float64'``: >>> ts = odl.rn((2, 3)) >>> ts rn((2, 3)) >>> ts.dtype dtype('float64') See Also -------- tensor_space : Space of tensors with arbitrary scalar data type. cn : Complex tensor space. """ rn_cls = tensor_space_impl(impl) if dtype is None: dtype = rn_cls.default_dtype(RealNumbers()) # Use args by keyword since the constructor may take other arguments # by position rn = rn_cls(shape=shape, dtype=dtype, **kwargs) if not rn.is_real: raise ValueError('data type {!r} not a real floating-point type.' ''.format(dtype)) return rn