def test_out_shape_from_meshgrid(): # 1d x = np.zeros(2) mg = sparse_meshgrid(x) assert out_shape_from_meshgrid(mg) == (2, ) # 3d x, y, z = np.zeros(2), np.zeros(3), np.zeros(4) mg = sparse_meshgrid(x, y, z) assert out_shape_from_meshgrid(mg) == (2, 3, 4) # 3d, fleshed out meshgrids x, y, z = np.zeros(2), np.zeros(3), np.zeros(4) mg = np.meshgrid(x, y, z, sparse=False, indexing='ij', copy=True) assert out_shape_from_meshgrid(mg) == (2, 3, 4) mg = np.meshgrid(x, y, z, sparse=False, indexing='ij', copy=True) mg = tuple(reversed([np.asfortranarray(arr) for arr in mg])) assert out_shape_from_meshgrid(mg) == (2, 3, 4)
def test_out_shape_from_meshgrid(): # 1d x = np.zeros(2) mg = sparse_meshgrid(x) assert out_shape_from_meshgrid(mg) == (2,) # 3d x, y, z = np.zeros(2), np.zeros(3), np.zeros(4) mg = sparse_meshgrid(x, y, z) assert out_shape_from_meshgrid(mg) == (2, 3, 4) # 3d, fleshed out meshgrids x, y, z = np.zeros(2), np.zeros(3), np.zeros(4) mg = np.meshgrid(x, y, z, sparse=False, indexing='ij', copy=True) assert out_shape_from_meshgrid(mg) == (2, 3, 4) mg = np.meshgrid(x, y, z, sparse=False, indexing='ij', copy=True) mg = tuple(reversed([np.asfortranarray(arr) for arr in mg])) assert out_shape_from_meshgrid(mg) == (2, 3, 4)
def one_vec(x, out=None): """The one function, vectorized.""" if is_valid_input_meshgrid(x, self.domain.ndim): order = meshgrid_input_order(x) else: order = 'C' out_shape = out_shape_from_meshgrid(x) if out is None: return np.ones(out_shape, dtype=dtype, order=order) else: out.fill(1)
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 _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 __call__(self, x, out=None): """Do the interpolation. Parameters ---------- x : `meshgrid` or `numpy.ndarray` Evaluation points of the interpolator out : `numpy.ndarray`, optional Array to which the results are written. Needs to have correct shape according to input ``x``. Returns ------- out : `numpy.ndarray` Interpolated values. If ``out`` was given, the returned object is a reference to it. """ ndim = len(self.coord_vecs) if self.input_type == 'array': # Make a (1, n) array from one with shape (n,) x = x.reshape([ndim, -1]) out_shape = out_shape_from_array(x) else: if len(x) != ndim: raise ValueError('number of vectors in x is {} instead of ' 'the grid dimension {}' ''.format(len(x), ndim)) out_shape = out_shape_from_meshgrid(x) if out is not None: if not isinstance(out, np.ndarray): raise TypeError('`out` {!r} not a `numpy.ndarray` ' 'instance'.format(out)) if out.shape != out_shape: raise ValueError('output shape {} not equal to expected ' 'shape {}'.format(out.shape, out_shape)) if out.dtype != self.values.dtype: raise ValueError('output dtype {} not equal to expected ' 'dtype {}' ''.format(out.dtype, self.values.dtype)) indices, norm_distances = self._find_indices(x) return self._evaluate(indices, norm_distances, out)
def _evaluate(self, indices, norm_distances, out=None): """Evaluate linear interpolation. Modified for in-place evaluation and treatment of out-of-bounds points by implicitly assuming 0 at the next node.""" # slice for broadcasting over trailing dimensions in self.values vslice = (slice(None),) + (None,) * (self.values.ndim - len(indices)) out_shape = out_shape_from_meshgrid(norm_distances) out_dtype = norm_distances[0].dtype if out is None: out = np.zeros(out_shape, dtype=out_dtype) else: out[:] = 0.0 # Weights and indices (per axis) low_weights, high_weights, edge_indices = _create_weight_edge_lists( indices, norm_distances, self.schemes, self.nn_variants) # Iterate over all possible combinations of [i, i+1] for each # axis, resulting in a loop of length 2**ndim for lo_hi, edge in zip(product(*([['l', 'h']] * len(indices))), product(*edge_indices)): weight = 1.0 # TODO: determine best summation order from array strides for lh, w_lo, w_hi in zip(lo_hi, low_weights, high_weights): # We don't multiply in place to exploit the cheap operations # in the beginning: sizes grow gradually as following: # (n, 1, 1, ...) -> (n, m, 1, ...) -> ... # Hence, it is faster to build up the weight array instead # of doing full-size operations from the beginning. if lh == 'l': weight = weight * w_lo else: weight = weight * w_hi out += np.asarray(self.values[edge]) * weight[vslice] return np.array(out, copy=False, ndmin=1)
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