Esempio n. 1
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
Esempio n. 2
0
def _sanitize_arguments(y, d, sigma, phi, eps, order, k=None):
    '''Sanitize input to RBFInterpolant and KNearestRBFInterpolant'''
    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('`eps` should be a scalar.')

    # If `phi` is not in `_MIN_ORDER`, then the RBF is either positive definite
    # (no minimum polynomial order) or user-defined (no known minimum
    # polynomial order)
    min_order = _MIN_ORDER.get(phi, -1)
    if order is None:
        order = max(min_order, 0)
    else:
        order = int(order)
        if order < -1:
            raise ValueError('`order` must be at least -1.')

        elif order < min_order:
            logger.warning(
                'The polynomial order should not be below %d when `phi` is '
                '%s. The interpolant may not be well-posed.' %
                (min_order, phi))

    nmonos = monomial_count(order, ndim)
    if k is None:
        nobs = ny
    else:
        # make sure the number of neighbors does not exceed the number of
        # observations.
        k = int(min(k, ny))
        nobs = k

    if nmonos > nobs:
        raise ValueError(
            'At least %d data points are required when `order` is %d and the '
            'number of dimensions is %d' % (nmonos, order, ndim))

    return y, d, sigma, phi, eps, order, k
Esempio n. 3
0
    def __init__(self, y, d, sigma=0.0, k=20, 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')

        # make sure the number of nearest neighbors used for interpolation does not
        # exceed the number of observations
        k = min(int(k), ny)

        phi = get_rbf(phi)
        if isinstance(phi, SparseRBF):
            raise ValueError('SparseRBF instances are not supported')

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

        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)
        nmonos = monomial_count(order, ndim)
        if nmonos > k:
            raise ValueError(
                'The polynomial order is too high. The number of monomials, %d, '
                'exceeds the number of neighbors used for interpolation, %d' %
                (nmonos, k))

        tree = KDTree(y)

        self.y = y
        self.d = d
        self.sigma = sigma
        self.k = k
        self.eps = eps
        self.phi = phi
        self.order = order
        self.tree = tree
Esempio n. 4
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
Esempio n. 5
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))
Esempio n. 6
0
File: fd.py Progetto: 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