def test_real(): C = ComplexNumbers() R = RealNumbers() Z = Integers() # __contains__ assert -1 in R assert 1 in R assert 0 in R assert -1.0 in R assert 1.0 in R assert 0.0 in R assert 2j not in R assert 2 + 2j not in R assert 'a' not in R # contains_set assert not R.contains_set(C) assert R.contains_set(R) assert C.contains_set(Z) # __eq__ assert R != C assert R == R assert R != Z # element assert C.element() == float(0.0) assert C.element(1) == float(1.0)
def _call(self, vf, out): """Implement ``self(vf, out)``.""" if self.domain.field == ComplexNumbers(): out.multiply(vf[0], self._vecfield[0].conj()) else: out.multiply(vf[0], self._vecfield[0]) if self.is_weighted: out *= self.weights[0] if self.domain.size == 1: return tmp = self.range.element() for vfi, gi, wi in zip(vf[1:], self.vecfield[1:], self.weights[1:]): if self.domain.field == ComplexNumbers(): tmp.multiply(vfi, gi.conj()) else: tmp.multiply(vfi, gi) if self.is_weighted: tmp *= wi out += tmp
def default_dtype(field=None): """Return the default of `Fn` data type for a given field. Parameters ---------- field : `Field` Set of numbers to be represented by a data type. Currently supported : `RealNumbers`, `ComplexNumbers` Returns ------- dtype : `type` Numpy data type specifier. The returned defaults are: ``RealNumbers()`` : ``np.dtype('float32')`` ``ComplexNumbers()`` : `NotImplemented` """ if field is None or field == RealNumbers(): return np.dtype('float32') elif field == ComplexNumbers(): return NotImplemented else: raise ValueError('no default data type defined for field {}' ''.format(field))
def test_integers(): C = ComplexNumbers() R = RealNumbers() Z = Integers() # __contains__ assert -1 in Z assert 1 in Z assert 0 in Z assert -1.0 not in Z assert 1.0 not in Z assert 0.0 not in Z assert 2j not in Z assert 2 + 2j not in Z assert 'a' not in Z # contains_set assert not Z.contains_set(C) assert not Z.contains_set(R) assert Z.contains_set(Z) # __eq__ assert Z != C assert Z != R assert Z == Z # element assert Z.element() == int(0) assert Z.element(1) == int(1) assert Z.element(1.5) == int(1.5)
def test_complex(): C = ComplexNumbers() R = RealNumbers() Z = Integers() # __contains__ assert -1 in C assert 1 in C assert 0 in C assert -1.0 in C assert 1.0 in C assert 0.0 in C assert 2j in C assert 2 + 2j in C assert 'a' not in C # contains_set assert C.contains_set(C) assert C.contains_set(R) assert C.contains_set(Z) # __eq__ assert C == C assert C != R assert C != Z # element assert C.element() == complex(0.0, 0.0) assert C.element(1) == complex(1.0, 0.0) assert C.element(1 + 2j) == complex(1.0, 2.0)
def __init__(self, shape, dtype): """Initialize a new instance. Parameters ---------- shape : nonnegative int or sequence of nonnegative ints Number of entries of type ``dtype`` per axis in this space. A single integer results in a space with rank 1, i.e., 1 axis. dtype : Data type of elements in this space. Can be provided in any way the `numpy.dtype` constructor understands, e.g. as built-in type or as a string. For a data type with a ``dtype.shape``, these extra dimensions are added *to the left* of ``shape``. """ # Handle shape and dtype, taking care also of dtypes with shape try: shape, shape_in = tuple(safe_int_conv(s) for s in shape), shape except TypeError: shape, shape_in = (safe_int_conv(shape), ), shape if any(s < 0 for s in shape): raise ValueError('`shape` must have only nonnegative entries, got ' '{}'.format(shape_in)) dtype = np.dtype(dtype) # We choose this order in contrast to Numpy, since we usually want # to represent discretizations of vector- or tensor-valued functions, # i.e., if dtype.shape == (3,) we expect f[0] to have shape `shape`. self.__shape = dtype.shape + shape self.__dtype = dtype.base if is_real_dtype(self.dtype): # real includes non-floating-point like integers field = RealNumbers() self.__real_dtype = self.dtype self.__real_space = self self.__complex_dtype = TYPE_MAP_R2C.get(self.dtype, None) self.__complex_space = None # Set in first call of astype elif is_complex_floating_dtype(self.dtype): field = ComplexNumbers() self.__real_dtype = TYPE_MAP_C2R[self.dtype] self.__real_space = None # Set in first call of astype self.__complex_dtype = self.dtype self.__complex_space = self else: field = None LinearSpace.__init__(self, field)
def derivative(self, vf): """Derivative of the point-wise norm operator at ``vf``. The derivative at ``F`` of the point-wise norm operator ``N`` with finite exponent ``p`` and weights ``w`` is the pointwise inner product with the vector field ``x --> N(F)(x)^(1-p) * [ F_j(x) * |F_j(x)|^(p-2) ]_j``. Note that this is not well-defined for ``F = 0``. If ``p < 2``, any zero component will result in a singularity. Parameters ---------- vf : domain `element-like` Vector field ``F`` at which to evaluate the derivative Returns ------- deriv : `PointwiseInner` Derivative operator at the given point ``vf`` Raises ------ NotImplementedError * if the vector field space is complex, since the derivative is not linear in that case * if the exponent is ``inf`` """ if self.domain.field == ComplexNumbers(): raise NotImplementedError('operator not Frechet-differentiable ' 'on a complex space') if self.exponent == float('inf'): raise NotImplementedError('operator not Frechet-differentiable ' 'for exponent = inf') vf = self.domain.element(vf) vf_pwnorm_fac = self(vf) vf_pwnorm_fac **= (self.exponent - 1) inner_vf = vf.copy() for gi in inner_vf: gi /= vf_pwnorm_fac * gi ** (self.exponent - 2) return PointwiseInner(self.domain, inner_vf, weight=self.weights)
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, size, dtype): """Initialize a new instance. Parameters ---------- size : `int` The number of dimensions of the space dtype : `object` 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 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 self._complex_dtype = _TYPE_MAP_R2C.get(self.dtype, None) self._complex_space = None # Set in first call of astype else: field = ComplexNumbers() self._is_real = False self._real_dtype = _TYPE_MAP_C2R[self.dtype] 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, 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 __init__(self, vfspace, vecfield, weight=None): """Initialize a new instance. Parameters ---------- vfspace : `ProductSpace` Space of vector fields on which the operator acts. It has to be a product space of identical spaces, i.e. a power space. vecfield : domain `element-like` Vector field with which to calculate the point-wise inner product of an input vector field weight : `array-like` or `float`, optional Weighting array or constant for the norm. If an array is given, its length must be equal to ``domain.size``, and all entries must be positive. A provided constant must be positive. By default, the weights are is taken from ``domain.weighting``. Note that this excludes unusual weightings with custom inner product, norm or dist. Examples -------- We make a tiny vector field space in 2D and create the point-wise inner product operator with a fixed vector field. The operator maps a vector field to a scalar function: >>> import odl >>> spc = odl.uniform_discr([-1, -1], [1, 1], (1, 2)) >>> vfspace = odl.ProductSpace(spc, 2) >>> fixed_vf = np.array([[[0, 1]], ... [[1, -1]]]) >>> pw_inner = PointwiseInner(vfspace, fixed_vf) >>> pw_inner.range == spc True Now we can calculate the inner product in each point: >>> x = vfspace.element([[[1, -4]], ... [[0, 3]]]) >>> print(pw_inner(x)) [[0.0, -7.0]] """ if not isinstance(vfspace, ProductSpace): raise TypeError('`vfsoace` {!r} is not a ProductSpace ' 'instance'.format(vfspace)) super().__init__(domain=vfspace, range=vfspace[0], linear=True) # Bail out if the space is complex but we cannot take the complex # conjugate. if (self.domain.field == ComplexNumbers() and not hasattr(self.base_space.element_type, 'conj')): raise NotImplementedError( 'base space element type {!r} does not implement conj() ' 'method required for complex inner products' ''.format(self.base_space.element_type)) self._vecfield = self.domain.element(vecfield) # Handle weighting, including sanity checks if weight is None: if hasattr(self.domain.weighting, 'vector'): self._weights = self.domain.weighting.vector elif hasattr(self.domain.weighting, 'const'): self._weights = (self.domain.weighting.const * np.ones(len(self.domain))) else: raise ValueError('weighting scheme {!r} of the domain does ' 'not define a weighting vector or constant' ''.format(self.domain.weighting)) elif np.isscalar(weight): if weight <= 0: raise ValueError('weighting constant must be positive, got ' '{}'.format(weight)) self._weights = float(weight) * np.ones(self.domain.size) else: self._weights = np.asarray(weight, dtype='float64') if (not np.all(self.weights > 0) or not np.all(np.isfinite(self.weights))): raise ValueError('weighting array {} contains invalid ' 'entries'.format(weight)) self._is_weighted = not np.array_equiv(self.weights, 1.0)