def test_is_valid_input_array(): # 1d valid_shapes = [(1, 1), (1, 2), (1, 20), (1,), (20,)] invalid_shapes = [(2, 1), (1, 1, 1), ()] for shp in valid_shapes: arr = np.zeros(shp) assert is_valid_input_array(arr, ndim=1) for shp in invalid_shapes: arr = np.zeros(shp) assert not is_valid_input_array(arr, ndim=1) # 3d valid_shapes = [(3, 1), (3, 2), (3, 20)] invalid_shapes = [(3,), (20,), (4, 1), (3, 1, 1), ()] for shp in valid_shapes: arr = np.zeros(shp) assert is_valid_input_array(arr, ndim=3) for shp in invalid_shapes: arr = np.zeros(shp) assert not is_valid_input_array(arr, ndim=3) # Other input invalid_input = [1, [[1, 2], [3, 4]], (5,)] for inp in invalid_input: assert not is_valid_input_array(inp, ndim=2)
def test_is_valid_input_array(): # 1d valid_shapes = [(1, 1), (1, 2), (1, 20), (20, )] invalid_shapes = [(2, 1), (1, 1, 1), (1, ), ()] for shp in valid_shapes: arr = np.zeros(shp) assert is_valid_input_array(arr, ndim=1) for shp in invalid_shapes: arr = np.zeros(shp) assert not is_valid_input_array(arr, ndim=1) # 3d valid_shapes = [(3, 1), (3, 2), (3, 20)] invalid_shapes = [(3, ), (20, ), (4, 1), (3, 1, 1), ()] for shp in valid_shapes: arr = np.zeros(shp) assert is_valid_input_array(arr, ndim=3) for shp in invalid_shapes: arr = np.zeros(shp) assert not is_valid_input_array(arr, ndim=3) # Other input assert is_valid_input_array([[1, 2], [3, 4]], ndim=2) invalid_input = [1, [[[1, 2], [3, 4]]], (5, )] for inp in invalid_input: assert not is_valid_input_array(inp, ndim=2)
def one_vec(x, out=None): """The one function, vectorized.""" if is_valid_input_meshgrid(x, self.domain.ndim): out_shape = out_shape_from_meshgrid(x) elif is_valid_input_array(x, self.domain.ndim): out_shape = out_shape_from_array(x) else: raise TypeError('invalid input type') if out is None: return np.ones(out_shape, dtype=self.out_dtype) else: out.fill(1)
def contains_all(self, other): """Test if all points defined by ``other`` are contained. Parameters ---------- other : object Can be a single point, a ``(d, N)`` array where ``d`` is the number of dimensions or a length-``d`` meshgrid sequence Returns ------- contains : `bool` `True` if all points are contained, `False` otherwise Examples -------- >>> b, e = [-1, 0, 2], [-0.5, 0, 3] >>> rbox = IntervalProd(b, e) ... ... # Arrays are expected in (ndim, npoints) shape >>> arr = np.array([[-1, 0, 2], # defining one point at a time ... [-0.5, 0, 2]]) >>> rbox.contains_all(arr.T) True >>> # Implicit meshgrids defined by coordinate vectors >>> from odl.discr.grid import sparse_meshgrid >>> vec1 = (-1, -0.9, -0.7) >>> vec2 = (0, 0, 0) >>> vec3 = (2.5, 2.75, 3) >>> mg = sparse_meshgrid(vec1, vec2, vec3) >>> rbox.contains_all(mg) True """ if other in self: return True elif is_valid_input_meshgrid(other, self.ndim): order = meshgrid_input_order(other) vecs = vecs_from_meshgrid(other, order) mins = np.fromiter((np.min(vec) for vec in vecs), dtype=float) maxs = np.fromiter((np.max(vec) for vec in vecs), dtype=float) return np.all(mins >= self.begin) and np.all(maxs <= self.end) elif is_valid_input_array(other, self.ndim): if self.ndim == 1: mins = np.min(other) maxs = np.max(other) else: mins = np.min(other, axis=1) maxs = np.max(other, axis=1) return np.all(mins >= self.begin) and np.all(maxs <= self.end) else: return False
def _default_out_of_place(func, dtype, x, **kwargs): """Default in-place evaluation method.""" if is_valid_input_array(x, func.domain.ndim): out_shape = out_shape_from_array(x) elif is_valid_input_meshgrid(x, func.domain.ndim): out_shape = out_shape_from_meshgrid(x) else: raise TypeError('cannot use in-place method to implement ' 'out-of-place non-vectorized evaluation.') if dtype is None: dtype = np.result_type(*x) out = np.empty(out_shape, dtype=dtype) func(x, out=out, **kwargs) return out
def _default_out_of_place(func, x, **kwargs): """Default in-place evaluation method.""" if is_valid_input_array(x, func.domain.ndim): out_shape = out_shape_from_array(x) elif is_valid_input_meshgrid(x, func.domain.ndim): out_shape = out_shape_from_meshgrid(x) else: raise TypeError('cannot use in-place method to implement ' 'out-of-place non-vectorized evaluation') dtype = func.space.out_dtype if dtype is None: dtype = np.result_type(*x) out = np.empty(out_shape, dtype=dtype) func(x, out=out, **kwargs) return out
def contains_all(self, other): """Test if all points defined by ``other`` are contained. Parameters ---------- other : Can be a single point, a ``(d, N)`` array where ``d`` is the number of dimensions or a length-``d`` meshgrid tuple Returns ------- contains : `bool` `True` if all points are contained, `False` otherwise Examples -------- >>> import odl >>> b, e = [-1, 0, 2], [-0.5, 0, 3] >>> rbox = IntervalProd(b, e) rrays are expected in (ndim, npoints) shape >>> arr = np.array([[-1, 0, 2], # defining one point at a time ... [-0.5, 0, 2]]) >>> rbox.contains_all(arr.T) True Implicit meshgrids defined by coordinate vectors >>> from odl.discr.grid import sparse_meshgrid >>> vec1 = (-1, -0.9, -0.7) >>> vec2 = (0, 0, 0) >>> vec3 = (2.5, 2.75, 3) >>> mg = sparse_meshgrid(vec1, vec2, vec3) >>> rbox.contains_all(mg) True Also works with any iterable >>> rbox.contains_all([[-1, -0.5], # define points by axis ... [0, 0], ... [2, 2]]) True And with grids >>> agrid = odl.uniform_sampling(rbox.begin, rbox.end, [3, 1, 3]) >>> rbox.contains_all(agrid) True """ # First try optimized methods if other in self: return True if hasattr(other, 'meshgrid'): return self.contains_all(other.meshgrid) elif is_valid_input_meshgrid(other, self.ndim): vecs = tuple(vec.squeeze() for vec in other) mins = np.fromiter((np.min(vec) for vec in vecs), dtype=float) maxs = np.fromiter((np.max(vec) for vec in vecs), dtype=float) return np.all(mins >= self.begin) and np.all(maxs <= self.end) # Convert to array and check each element other = np.asarray(other) if is_valid_input_array(other, self.ndim): if self.ndim == 1: mins = np.min(other) maxs = np.max(other) else: mins = np.min(other, axis=1) maxs = np.max(other, axis=1) return np.all(mins >= self.begin) and np.all(maxs <= self.end) else: return False
def contains_all(self, other, atol=0.0): """Return ``True`` if all points defined by ``other`` are contained. Parameters ---------- other : Collection of points to be tested. Can be given as a single point, a ``(d, N)`` array-like where ``d`` is the number of dimensions, or a length-``d`` `meshgrid` tuple. atol : float, optional The maximum allowed distance in 'inf'-norm between the other set and this interval product. Returns ------- contains : bool ``True`` if all points are contained, ``False`` otherwise. Examples -------- >>> min_pt, max_pt = [-1, 0, 2], [-0.5, 0, 3] >>> rbox = IntervalProd(min_pt, max_pt) Arrays are expected in ``(ndim, npoints)`` shape: >>> arr = np.array([[-1, 0, 2], # defining one point at a time ... [-0.5, 0, 2]]) >>> rbox.contains_all(arr.T) True Implicit meshgrids defined by coordinate vectors: >>> from odl.discr.grid import sparse_meshgrid >>> vec1 = (-1, -0.9, -0.7) >>> vec2 = (0, 0, 0) >>> vec3 = (2.5, 2.75, 3) >>> mg = sparse_meshgrid(vec1, vec2, vec3) >>> rbox.contains_all(mg) True Works also with an arbitrary iterable: >>> rbox.contains_all([[-1, -0.5], # define points by axis ... [0, 0], ... [2, 2]]) True Grids are also accepted as input: >>> agrid = odl.uniform_sampling(rbox.min_pt, rbox.max_pt, [3, 1, 3]) >>> rbox.contains_all(agrid) True """ atol = float(atol) # First try optimized methods if other in self: return True if hasattr(other, 'meshgrid'): return self.contains_all(other.meshgrid, atol=atol) elif is_valid_input_meshgrid(other, self.ndim): vecs = tuple(vec.squeeze() for vec in other) mins = np.fromiter((np.min(vec) for vec in vecs), dtype=float) maxs = np.fromiter((np.max(vec) for vec in vecs), dtype=float) return (np.all(mins >= self.min_pt - atol) and np.all(maxs <= self.max_pt + atol)) # Convert to array and check each element other = np.asarray(other) if is_valid_input_array(other, self.ndim): if self.ndim == 1: mins = np.min(other) maxs = np.max(other) else: mins = np.min(other, axis=1) maxs = np.max(other, axis=1) return np.all(mins >= self.min_pt) and np.all(maxs <= self.max_pt) else: return False
def __call__(self, x, out=None, **kwargs): """Evaluate the function at one or multiple values ``x``. Parameters ---------- x : object Input argument for the function evaluation. Conditions on ``x`` depend on vectorization: `False` : ``x`` must be a domain element `True` : ``x`` must be a `numpy.ndarray` with shape ``(d, N)``, where ``d`` is the number of dimensions of the function domain OR ``x`` is a sequence of `numpy.ndarray` with length ``space.ndim``, and the arrays can be broadcast against each other. out : `numpy.ndarray`, optional Output argument holding the result of the function evaluation, can only be used for vectorized functions. Its shape must be equal to ``np.broadcast(*x).shape``. bounds_check : bool Whether or not to check if all input points lie in the function domain. For vectorized evaluation, this requires the domain to implement `Set.contains_all`. Default: `True` Returns ------- out : range element or array of elements Result of the function evaluation. If ``out`` was provided, the returned object is a reference to it. Raises ------ TypeError If ``x`` is not a valid vectorized evaluation argument If ``out`` is not a range element or a `numpy.ndarray` of range elements ValueError If evaluation points fall outside the valid domain """ bounds_check = kwargs.pop('bounds_check', True) if bounds_check and not hasattr(self.domain, 'contains_all'): raise AttributeError('bounds check not possible for ' 'domain {}, missing `contains_all()` ' 'method.'.format(self.domain)) # Check for input type and determine output shape if is_valid_input_array(x, self.domain.ndim): out_shape = out_shape_from_array(x) scalar_out = False # For 1d, squeeze the array if self.domain.ndim == 1 and x.ndim == 2: x = x.squeeze() elif is_valid_input_meshgrid(x, self.domain.ndim): out_shape = out_shape_from_meshgrid(x) scalar_out = False # For 1d, fish out the vector from the tuple if self.domain.ndim == 1: x = x[0] elif x in self.domain: x = np.atleast_2d(x).T # make a (d, 1) array out_shape = (1,) scalar_out = (out is None) else: # Unknown input raise TypeError('argument {!r} not a valid vectorized ' 'input. Expected an element of the domain ' '{dom}, a ({dom.ndim}, n) array ' 'or a length-{dom.ndim} meshgrid sequence.' ''.format(x, dom=self.domain)) # Check bounds if specified if bounds_check: if not self.domain.contains_all(x): raise ValueError('input contains points outside ' 'the domain {}.'.format(self.domain)) # Call the function and check out shape, before or after if out is None: out = self._call(x, **kwargs) if out_shape != (1,) and out.shape != out_shape: raise ValueError('output shape {} not equal to shape ' '{} expected from input.' ''.format(out.shape, out_shape)) else: if not isinstance(out, np.ndarray): raise TypeError('output {!r} not a `numpy.ndarray` ' 'instance.') if out_shape != (1,) and out.shape != out_shape: raise ValueError('output shape {} not equal to shape ' '{} expected from input.' ''.format(out.shape, out_shape)) self._call(x, out=out, **kwargs) # Check output values if bounds_check: if not self.range.contains_all(out): raise ValueError('output contains points outside ' 'the range {}.' ''.format(self.domain)) # Numpy does not implement __complex__ for arrays (in contrast to # __float__), so we have to fish out the scalar ourselves. return self.range.element(out.ravel()[0]) if scalar_out else out
def __call__(self, x, out=None, **kwargs): """Return ``self(x[, out, **kwargs])``. Parameters ---------- x : domain `element-like`, `meshgrid` or `numpy.ndarray` Input argument for the function evaluation. Conditions on ``x`` depend on its type: element-like: must be a castable to a domain element meshgrid: length must be ``space.ndim``, and the arrays must be broadcastable against each other. array: shape must be ``(d, N)``, where ``d`` is the number of dimensions of the function domain out : `numpy.ndarray`, optional Output argument holding the result of the function evaluation, can only be used for vectorized functions. Its shape must be equal to ``np.broadcast(*x).shape``. Other Parameters ---------------- bounds_check : `bool` If `True`, check if all input points lie in the function domain in the case of vectorized evaluation. This requires the domain to implement `Set.contains_all`. Default: `True` Returns ------- out : range element or array of elements Result of the function evaluation. If ``out`` was provided, the returned object is a reference to it. Raises ------ TypeError If ``x`` is not a valid vectorized evaluation argument If ``out`` is not a range element or a `numpy.ndarray` of range elements ValueError If evaluation points fall outside the valid domain """ bounds_check = kwargs.pop('bounds_check', True) if bounds_check and not hasattr(self.domain, 'contains_all'): raise AttributeError('bounds check not possible for ' 'domain {}, missing `contains_all()` ' 'method'.format(self.domain)) if bounds_check and not hasattr(self.range, 'contains_all'): raise AttributeError('bounds check not possible for ' 'range {}, missing `contains_all()` ' 'method'.format(self.range)) ndim = getattr(self.domain, 'ndim', None) # Check for input type and determine output shape if is_valid_input_meshgrid(x, ndim): out_shape = out_shape_from_meshgrid(x) scalar_out = False # Avoid operations on tuples like x * 2 by casting to array if ndim == 1: x = x[0][None, ...] elif is_valid_input_array(x, ndim): x = np.asarray(x) out_shape = out_shape_from_array(x) scalar_out = False # For 1d, squeeze the array if ndim == 1 and x.ndim == 2: x = x.squeeze() elif x in self.domain: x = np.atleast_2d(x).T # make a (d, 1) array out_shape = (1,) scalar_out = (out is None) else: # Unknown input txt_1d = ' or (n,)' if ndim == 1 else '' raise TypeError('Argument {!r} not a valid vectorized ' 'input. Expected an element of the domain ' '{domain}, an array-like with shape ' '({domain.ndim}, n){} or a length-{domain.ndim} ' 'meshgrid tuple.' ''.format(x, txt_1d, domain=self.domain)) # Check bounds if specified if bounds_check: if not self.domain.contains_all(x): raise ValueError('input contains points outside ' 'the domain {}'.format(self.domain)) # Call the function and check out shape, before or after if out is None: if ndim == 1: try: out = self._call(x, **kwargs) if np.ndim(out) == 0 and not scalar_out: # Don't accept scalar result. A typical situation where # this occurs is with comparison operators, e.g. # "return x > 0" which simply gives 'True' for a # non-empty tuple (in Python 2). We raise TypeError # to trigger the call with x[0]. raise TypeError out = np.atleast_1d(np.squeeze(out)) except (TypeError, IndexError): # TypeError is raised if a meshgrid was used but the # function expected an array (1d only). In this case we try # again with the first meshgrid vector. # IndexError is raised in expressions like x[x > 0] since # "x > 0" evaluates to 'True', i.e. 1, and that index is # out of range for a meshgrid tuple of length 1 :-). To get # the real errors with indexing, we check again for the # same scenario (scalar output when not valid) as in the # first case. out = self._call(x[0], **kwargs) if np.ndim(out) == 0 and not scalar_out: raise ValueError('invalid scalar output') out = np.atleast_1d(np.squeeze(out)) else: out = self._call(x, **kwargs) if self.out_dtype is not None: # Cast to proper dtype if needed out = out.astype(self.out_dtype) if out_shape != (1,) and out.shape != out_shape: # Try to broadcast the returned element if possible try: out = np.broadcast_to(out, out_shape) except AttributeError: # The above requires numpy 1.10, fallback impl else shape = [m if n == 1 and m != 1 else 1 for n, m in zip(out.shape, out_shape)] out = out + np.zeros(shape, dtype=out.dtype) else: if not isinstance(out, np.ndarray): raise TypeError('output {!r} not a `numpy.ndarray` ' 'instance') if out_shape != (1,) and out.shape != out_shape: raise ValueError('output shape {} not equal to shape ' '{} expected from input' ''.format(out.shape, out_shape)) if self.out_dtype is not None and out.dtype != self.out_dtype: raise ValueError('`out.dtype` ({}) does not match out_dtype ' '({})'.format(out.dtype, self.out_dtype)) if ndim == 1: # TypeError for meshgrid in 1d, but expected array (see above) try: self._call(x, out=out, **kwargs) except TypeError: self._call(x[0], out=out, **kwargs) else: self._call(x, out=out, **kwargs) # Check output values if bounds_check: if not self.range.contains_all(out): raise ValueError('output contains points outside ' 'the range {}' ''.format(self.range)) # Numpy does not implement __complex__ for arrays (in contrast to # __float__), so we have to fish out the scalar ourselves. return self.range.element(out.ravel()[0]) if scalar_out else out
def contains_all(self, other, atol=0.0): """Test if all points defined by ``other`` are contained. Parameters ---------- other : Can be a single point, a ``(d, N)`` array where ``d`` is the number of dimensions or a length-``d`` meshgrid tuple atol : `float`, optional The maximum allowed distance in 'inf'-norm between the other set and this interval product. Returns ------- contains : `bool` `True` if all points are contained, `False` otherwise Examples -------- >>> import odl >>> b, e = [-1, 0, 2], [-0.5, 0, 3] >>> rbox = IntervalProd(b, e) rrays are expected in (ndim, npoints) shape >>> arr = np.array([[-1, 0, 2], # defining one point at a time ... [-0.5, 0, 2]]) >>> rbox.contains_all(arr.T) True Implicit meshgrids defined by coordinate vectors >>> from odl.discr.grid import sparse_meshgrid >>> vec1 = (-1, -0.9, -0.7) >>> vec2 = (0, 0, 0) >>> vec3 = (2.5, 2.75, 3) >>> mg = sparse_meshgrid(vec1, vec2, vec3) >>> rbox.contains_all(mg) True Also works with any iterable >>> rbox.contains_all([[-1, -0.5], # define points by axis ... [0, 0], ... [2, 2]]) True And with grids >>> agrid = odl.uniform_sampling(rbox.begin, rbox.end, [3, 1, 3]) >>> rbox.contains_all(agrid) True """ atol = float(atol) # First try optimized methods if other in self: return True if hasattr(other, 'meshgrid'): return self.contains_all(other.meshgrid, atol=atol) elif is_valid_input_meshgrid(other, self.ndim): vecs = tuple(vec.squeeze() for vec in other) mins = np.fromiter((np.min(vec) for vec in vecs), dtype=float) maxs = np.fromiter((np.max(vec) for vec in vecs), dtype=float) return (np.all(mins >= self.begin - atol) and np.all(maxs <= self.end + atol)) # Convert to array and check each element other = np.asarray(other) if is_valid_input_array(other, self.ndim): if self.ndim == 1: mins = np.min(other) maxs = np.max(other) else: mins = np.min(other, axis=1) maxs = np.max(other, axis=1) return np.all(mins >= self.begin) and np.all(maxs <= self.end) else: return False