Exemplo n.º 1
0
    def __init__(self, y, d, sigma=0.0, phi='phs3', eps=1.0, order=None):
        y, d, sigma, phi, eps, order, _ = _sanitize_arguments(
            y, d, sigma, phi, eps, order, None)

        # For improved numerical stability, shift the observations so that
        # their centroid is at zero
        center = y.mean(axis=0)
        y = y - center
        # Build the system of equations and solve for the RBF and mononomial
        # coefficients
        Kyy = phi(y, y, eps=eps)
        if sp.issparse(Kyy):
            Kyy = sp.csc_matrix(Kyy + sp.diags(sigma**2))
        else:
            Kyy[range(y.shape[0]), range(y.shape[0])] += sigma**2

        Py = mvmonos(y, order)
        phi_coeff, poly_coeff = PartitionedSolver(Kyy, Py).solve(d)

        self.y = y
        self.phi = phi
        self.eps = eps
        self.order = order
        self.center = center
        self.phi_coeff = phi_coeff
        self.poly_coeff = poly_coeff
Exemplo n.º 2
0
    def __init__(self, y, d, sigma=0.0, phi='phs3', eps=1.0, order=None):
        y = np.asarray(y, dtype=float)
        assert_shape(y, (None, None), 'y')
        ny, ndim = y.shape

        d = np.asarray(d, dtype=float)
        assert_shape(d, (ny, ), 'd')

        if np.isscalar(sigma):
            sigma = np.full(ny, sigma, dtype=float)
        else:
            sigma = np.asarray(sigma, dtype=float)
            assert_shape(sigma, (ny, ), 'sigma')

        phi = get_rbf(phi)

        if not np.isscalar(eps):
            raise ValueError('The shape parameter should be a float')

        # If `phi` is not in `_MIN_ORDER`, then the RBF is either positive definite
        # (no minimum polynomial order) or user-defined
        min_order = _MIN_ORDER.get(phi, -1)
        if order is None:
            order = max(min_order, 0)
        elif order < min_order:
            logger.warning(
                'The polynomial order should not be below %d for %s in order for the '
                'interpolant to be well-posed' % (min_order, phi))

        order = int(order)
        # For improved numerical stability, shift the observations so that their
        # centroid is at zero
        center = y.mean(axis=0)
        y = y - center
        # Build the system of equations and solve for the RBF and mononomial
        # coefficients
        Kyy = phi(y, y, eps=eps)
        S = scipy.sparse.diags(sigma**2)
        Py = mvmonos(y, order)
        nmonos = Py.shape[1]
        if nmonos > ny:
            raise ValueError(
                'The polynomial order is too high. The number of monomials, %d, '
                'exceeds the number of observations, %d' % (nmonos, ny))

        z = np.zeros(nmonos, dtype=float)
        phi_coeff, poly_coeff = PartitionedSolver(Kyy + S, Py).solve(d, z)

        self.y = y
        self.phi = phi
        self.eps = eps
        self.order = order
        self.center = center
        self.phi_coeff = phi_coeff
        self.poly_coeff = poly_coeff
Exemplo n.º 3
0
    def __init__(self,
                 y,
                 d,
                 sigma=None,
                 eps=1.0,
                 phi=phs3,
                 order=1,
                 extrapolate=True):
        y = np.asarray(y)
        assert_shape(y, (None, None), 'y')
        nobs, dim = y.shape

        d = np.asarray(d)
        assert_shape(d, (nobs, ), 'd')

        if sigma is None:
            # if sigma is not specified then it is zeros
            sigma = np.zeros(nobs)

        elif np.isscalar(sigma):
            # if a float is specified then use it as the uncertainties for
            # all observations
            sigma = np.repeat(sigma, nobs)

        else:
            sigma = np.asarray(sigma)
            assert_shape(sigma, (nobs, ), 'sigma')

        phi = get_rbf(phi)

        # form block consisting of the RBF and uncertainties on the
        # diagonal
        K = phi(y, y, eps=eps)
        Cd = scipy.sparse.diags(sigma**2)
        # form the block consisting of the monomials
        pwr = powers(order, dim)
        P = mvmonos(y, pwr)
        # create zeros vector for the right-hand-side
        z = np.zeros((pwr.shape[0], ))
        # solve for the RBF and mononomial coefficients
        phi_coeff, poly_coeff = PartitionedSolver(K + Cd, P).solve(d, z)

        self._y = y
        self._phi = phi
        self._order = order
        self._eps = eps
        self._phi_coeff = phi_coeff
        self._poly_coeff = poly_coeff
        self._pwr = pwr
        self.extrapolate = extrapolate
Exemplo n.º 4
0
    def __call__(self, x, diff=None, chunk_size=1000):
        ''' 
    Evaluates the interpolant at `x`

    Parameters 
    ---------- 
    x : (N, D) array
      Target points.

    diff : (D,) int array, optional
      Derivative order for each spatial dimension.
        
    chunk_size : int, optional  
      Break `x` into chunks with this size and evaluate the
      interpolant for each chunk.  Smaller values result in decreased
      memory usage but also decreased speed.

    Returns
    -------
    out : (N,) array
      Values of the interpolant at `x`
      
    '''
        x = np.asarray(x, dtype=float)
        assert_shape(x, (None, self._y.shape[1]), 'x')

        xlen = x.shape[0]
        # allocate output array
        out = np.zeros(xlen, dtype=float)
        count = 0
        while count < xlen:
            start, stop = count, count + chunk_size
            K = self._phi(x[start:stop], self._y, eps=self._eps, diff=diff)
            P = mvmonos(x[start:stop], self._pwr, diff=diff)
            out[start:stop] = (K.dot(self._phi_coeff) +
                               P.dot(self._poly_coeff))
            count += chunk_size

        # return zero for points outside of the convex hull if
        # extrapolation is not allowed
        if not self.extrapolate:
            out[~_in_hull(x, self._y)] = np.nan

        return out
Exemplo n.º 5
0
    def __call__(self, x, diff=None, chunk_size=1000):
        '''
    Evaluates the interpolant at `x`

    Parameters
    ----------
    x : (N, D) float array
      Target points

    diff : (D,) int array, optional
      Derivative order for each spatial dimension

    chunk_size : int, optional
      Break `x` into chunks with this size and evaluate the interpolant for
      each chunk

    Returns
    -------
    (N,) float array

    '''
        x = np.asarray(x, dtype=float)
        assert_shape(x, (None, self.y.shape[1]), 'x')
        nx = x.shape[0]

        if chunk_size is not None:
            out = np.zeros(nx, dtype=float)
            for start in range(0, nx, chunk_size):
                stop = start + chunk_size
                out[start:stop] = self(x[start:stop],
                                       diff=diff,
                                       chunk_size=None)

            return out

        x = x - self.center
        Kxy = self.phi(x, self.y, eps=self.eps, diff=diff)
        Px = mvmonos(x, self.order, diff=diff)
        out = Kxy.dot(self.phi_coeff) + Px.dot(self.poly_coeff)
        return out
Exemplo n.º 6
0
    def __call__(self, x, diff=None, chunk_size=100):
        '''
    Evaluates the interpolant at `x`

    Parameters
    ----------
    x : (N, D) float array
      Target points

    diff : (D,) int array, optional
      Derivative order for each spatial dimension

    chunk_size : int, optional
      Break `x` into chunks with this size and evaluate the interpolant for
      each chunk

    Returns
    -------
    (N,) float array

    '''
        x = np.asarray(x, dtype=float)
        assert_shape(x, (None, self.y.shape[1]), 'x')
        nx = x.shape[0]

        if chunk_size is not None:
            out = np.zeros(nx, dtype=float)
            for start in range(0, nx, chunk_size):
                stop = start + chunk_size
                out[start:stop] = self(x[start:stop],
                                       diff=diff,
                                       chunk_size=None)

            return out

        # get the indices of the k-nearest observations for each interpolation
        # point
        _, nbr = self.tree.query(x, self.k)
        # multiple interpolation points may have the same neighborhood. Make the
        # neighborhoods unique so that we only compute the interpolation
        # coefficients once for each neighborhood
        nbr, inv = np.unique(np.sort(nbr, axis=1), return_inverse=True, axis=0)
        nnbr = nbr.shape[0]
        # Get the observation data for each neighborhood
        y, d, sigma = self.y[nbr], self.d[nbr], self.sigma[nbr]
        # shift the centers of each neighborhood to zero for numerical stability
        centers = y.mean(axis=1)
        y = y - centers[:, None]
        # build the left-hand-side interpolation matrix consisting of the RBF
        # and monomials evaluated at each neighborhood
        Kyy = self.phi(y, y, eps=self.eps)
        Kyy[:, range(self.k), range(self.k)] += sigma**2
        Py = mvmonos(y, self.order)
        PyT = np.transpose(Py, (0, 2, 1))
        nmonos = Py.shape[2]
        Z = np.zeros((nnbr, nmonos, nmonos), dtype=float)
        LHS = np.block([[Kyy, Py], [PyT, Z]])
        # build the right-hand-side data vector consisting of the observations for
        # each neighborhood and extra zeros
        z = np.zeros((nnbr, nmonos), dtype=float)
        rhs = np.hstack((d, z))
        # solve for the RBF and polynomial coefficients for each neighborhood
        coeff = np.linalg.solve(LHS, rhs)
        # expand the arrays from having one entry per neighborhood to one entry per
        # interpolation point
        coeff = coeff[inv]
        y = y[inv]
        centers = centers[inv]
        # evaluate at the interpolation points
        x = x - centers
        phi_coeff = coeff[:, :self.k]
        poly_coeff = coeff[:, self.k:]
        Kxy = self.phi(x[:, None], y, eps=self.eps, diff=diff)[:, 0]
        Px = mvmonos(x, self.order, diff=diff)
        out = (Kxy * phi_coeff).sum(axis=1) + (Px * poly_coeff).sum(axis=1)
        return out
Exemplo n.º 7
0
def weights(x, s, diffs, coeffs=None, phi=phs3, order=None, eps=1.0):
    '''
  Returns the weights which map a functions values at `s` to an approximation
  of that functions derivative at `x`. The weights are computed using the
  RBF-FD method described in [1]. In this function `x` is a single point in
  D-dimensional space. Use `weight_matrix` to compute the weights for multiple
  point.

  Parameters
  ----------
  x : (D,) array
    Target point. The weights will approximate the derivative at this point.

  s : (N, D) array
    Stencil points. The derivative will be approximated with a weighted sum of
    values at this point.

  diffs : (D,) int array or (K, D) int array
    Derivative orders for each spatial dimension. For example `[2, 0]`
    indicates that the weights should approximate the second derivative with
    respect to the first spatial dimension in two-dimensional space.  diffs can
    also be a (K, D) array, where each (D,) sub-array is a term in a
    differential operator. For example the two-dimensional Laplacian can be
    represented as `[[2, 0], [0, 2]]`.

  coeffs : (K,) array, optional
    Coefficients for each term in the differential operator specified with
    `diffs`.  Defaults to an array of ones. If `diffs` was specified as a (D,)
    array then `coeffs` should be a length 1 array.

  phi : rbf.basis.RBF instance or str, optional
    Type of RBF. Select from those available in `rbf.basis` or create your own.

  order : int, optional
    Order of the added polynomial. This defaults to the highest derivative
    order. For example, if `diffs` is `[[2, 0], [0, 1]]`, then order is set to
    2.

  eps : float or (N,) array, optional
    Shape parameter for each RBF, which have centers `s`. This only makes a
    difference when using RBFs that are not scale invariant. All the predefined
    RBFs except for the odd order polyharmonic splines are not scale invariant.

  Returns
  -------
  out : (N,) array
    RBF-FD weights

  Examples
  --------
  Calculate the weights for a one-dimensional second order derivative.

  >>> x = np.array([1.0])
  >>> s = np.array([[0.0], [1.0], [2.0]])
  >>> diff = (2,)
  >>> weights(x, s, diff)
  array([ 1., -2., 1.])

  Calculate the weights for estimating an x derivative from three points in a
  two-dimensional plane

  >>> x = np.array([0.25, 0.25])
  >>> s = np.array([[0.0, 0.0],
                    [1.0, 0.0],
                    [0.0, 1.0]])
  >>> diff = (1, 0)
  >>> weights(x, s, diff)
  array([ -1., 1., 0.])

  References
  ----------
  [1] Fornberg, B. and N. Flyer. A Primer on Radial Basis
  Functions with Applications to the Geosciences. SIAM, 2015.

  '''
    x = np.asarray(x, dtype=float)
    assert_shape(x, (None, ), 'x')

    s = np.asarray(s, dtype=float)
    assert_shape(s, (None, x.shape[0]), 's')

    diffs = np.asarray(diffs, dtype=int)
    diffs = _reshape_diffs(diffs)

    if coeffs is None:
        coeffs = np.ones(diffs.shape[0], dtype=float)
    else:
        coeffs = np.asarray(coeffs, dtype=float)
        assert_shape(coeffs, (diffs.shape[0], ), 'coeffs')

    phi = get_rbf(phi)
    # stencil size and number of dimensions
    size, dim = s.shape
    # get the maximum polynomial order allowed for this stencil size
    max_order = _max_poly_order(size, dim)
    if order is None:
        # If the polynomial order is not specified, make it equal to the derivative
        # order, provided that the stencil size is large enough.
        order = _default_poly_order(diffs)
        order = min(order, max_order)

    if order > max_order:
        raise ValueError('Polynomial order is too high for the stencil size')

    # center the stencil on `x` for improved numerical stability
    s = s - x
    x = np.zeros_like(x)
    # get the powers for the added monomials
    pwr = powers(order, dim)
    # evaluate the RBF and monomials at each point in the stencil. This becomes
    # the left-hand-side
    A = phi(s, s, eps=eps)
    P = mvmonos(s, pwr)
    # Evaluate the RBF and monomials for each term in the differential operator.
    # This becomes the right-hand-side.
    a = np.zeros((1, size), dtype=float)
    p = np.zeros((1, pwr.shape[0]), dtype=float)
    for c, d in zip(coeffs, diffs):
        a += c * phi(x[None, :], s, eps=eps, diff=d)
        p += c * mvmonos(x[None, :], pwr, diff=d)

    # squeeze `a` and `p` into 1d arrays. `a` is ran through as_array because it
    # may be sparse.
    a = as_array(a)[0]
    p = p[0]

    # attempt to compute the RBF-FD weights
    try:
        w = PartitionedSolver(A, P).solve(a, p)[0]
        return w

    except np.linalg.LinAlgError:
        raise np.linalg.LinAlgError(
            'An error was raised while computing the RBF-FD weights at point %s '
            'with the RBF %s and the polynomial order %s. This may be due to a '
            'stencil with duplicate or collinear points. The stencil contains the '
            'following points:\n%s' % (x, phi, order, s))
Exemplo n.º 8
0
Arquivo: fd.py Projeto: ejnnr/RBF
def weights(x, s, diffs,
            coeffs=None,
            phi=phs3,
            order=None,
            eps=1.0):
  '''
  Returns the weights which map a function's values at `s` to an approximation
  of that function's derivative at `x`. The weights are computed using the
  RBF-FD method described in [1]

  Parameters
  ----------
  x : (..., D) float array
    Target points where the derivative is being approximated

  s : (..., M, D) float array
    Stencils for each target point

  diffs : (D,) int array or (K, D) int array
    Derivative orders for each spatial dimension. For example `[2, 0]`
    indicates that the weights should approximate the second derivative with
    respect to the first spatial dimension in two-dimensional space.  `diffs`
    can also be a (K, D) array, where each (D,) sub-array is a term in a
    differential operator. For example, the two-dimensional Laplacian can be
    represented as `[[2, 0], [0, 2]]`.

  coeffs : (K, ...) float array, optional
    Coefficients for each term in the differential operator specified with
    `diffs`. The coefficients can vary between target points. Defaults to an
    array of ones.

  phi : rbf.basis.RBF instance or str, optional
    Type of RBF. See `rbf.basis` for the available options.

  order : int, optional
    Order of the added polynomial. This defaults to the highest derivative
    order. For example, if `diffs` is `[[2, 0], [0, 1]]`, then this is set to
    2.

  eps : float or float array, optional
    Shape parameter for each RBF

  Returns
  -------
  (..., M) float array
    RBF-FD weights for each target point

  Examples
  --------
  Calculate the weights for a one-dimensional second order derivative.

  >>> x = np.array([1.0])
  >>> s = np.array([[0.0], [1.0], [2.0]])
  >>> diff = (2,)
  >>> weights(x, s, diff)
  array([ 1., -2., 1.])

  Calculate the weights for estimating an x derivative from three points in a
  two-dimensional plane

  >>> x = np.array([0.25, 0.25])
  >>> s = np.array([[0.0, 0.0],
                    [1.0, 0.0],
                    [0.0, 1.0]])
  >>> diff = (1, 0)
  >>> weights(x, s, diff)
  array([ -1., 1., 0.])

  References
  ----------
  [1] Fornberg, B. and N. Flyer. A Primer on Radial Basis Functions with
  Applications to the Geosciences. SIAM, 2015.

  '''
  x = np.asarray(x, dtype=float)
  assert_shape(x, (..., None), 'x')
  bcast = x.shape[:-1]
  ndim = x.shape[-1]

  s = np.asarray(s, dtype=float)
  assert_shape(s, (..., None, ndim), 's')
  # broadcast leading dimensions of `s` to match leading dimensions of `x`
  s = np.broadcast_to(s, bcast + s.shape[-2:])
  ssize = s.shape[-2]

  diffs = np.asarray(diffs, dtype=int)
  diffs = np.atleast_2d(diffs)
  assert_shape(diffs, (None, ndim), 'diffs')

  if coeffs is None:
    coeffs = np.ones(len(diffs), dtype=float)
  else:
    coeffs = np.asarray(coeffs, dtype=float)
    assert_shape(coeffs, (len(diffs), ...), 'coeffs')

  # broadcast each element in `coeffs` to match leading dimensions of `x`
  coeffs = [np.broadcast_to(c, bcast) for c in coeffs]

  phi = get_rbf(phi)

  # get the maximum polynomial order allowed for this stencil size
  max_order = _max_poly_order(ssize, ndim)
  if order is None:
    # If the polynomial order is not specified, make it equal to the derivative
    # order, provided that the stencil size is large enough.
    order = diffs.sum(axis=1).max()
    order = min(order, max_order)

  if order > max_order:
    raise ValueError('Polynomial order is too high for the stencil size')

  # center the stencil on `x` for improved numerical stability
  x = x[..., None, :]
  s = s - x
  x = np.zeros_like(x)
  # get the powers for the added monomials
  pwr = monomial_powers(order, ndim)
  # evaluate the RBF and monomials at each point in the stencil. This becomes
  # the left-hand-side
  A = phi(s, s, eps=eps)
  P = mvmonos(s, pwr)
  Pt = np.einsum('...ij->...ji', P)
  Z = np.zeros(bcast + (len(pwr), len(pwr)), dtype=float)
  LHS = np.concatenate(
    (np.concatenate((A, P), axis=-1),
     np.concatenate((Pt, Z), axis=-1)),
    axis=-2)

  # Evaluate the RBF and monomials at the target points for each term in the
  # differential operator. This becomes the right-hand-side.
  a, p = 0.0, 0.0
  for c, d in zip(coeffs, diffs):
    a += c[..., None, None]*phi(x, s, eps=eps, diff=d)
    p += c[..., None, None]*mvmonos(x, pwr, diff=d)

  # convert `a` to an array because phi may be a sparse RBF
  a = as_array(a)[..., 0, :]
  p = p[..., 0, :]
  rhs = np.concatenate((a, p), axis=-1)

  w = np.linalg.solve(LHS, rhs)[..., :ssize]
  return w