Esempio n. 1
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. 2
0
        return out


def clear_rbf_caches():
    '''
    Clear the caches of numerical functions for all the RBF instances
    '''
    for inst in RBF._INSTANCES:
        if inst() is not None:
            inst().clear_cache()


## Instantiate some common RBFs
#####################################################################
_phs8_limits = {}
_phs8_limits.update((tuple(i), 0) for i in powers(7, 1))
_phs8_limits.update((tuple(i), 0) for i in powers(7, 2))
_phs8_limits.update((tuple(i), 0) for i in powers(7, 3))
phs8 = RBF((_EPS * _R)**8 * sympy.log(_EPS * _R),
           tol=1e-10,
           limits=_phs8_limits)

_phs7_limits = {}
_phs7_limits.update((tuple(i), 0) for i in powers(6, 1))
_phs7_limits.update((tuple(i), 0) for i in powers(6, 2))
_phs7_limits.update((tuple(i), 0) for i in powers(6, 3))
phs7 = RBF((_EPS * _R)**7, tol=1e-10, limits=_phs7_limits)

_phs6_limits = {}
_phs6_limits.update((tuple(i), 0) for i in powers(5, 1))
_phs6_limits.update((tuple(i), 0) for i in powers(5, 2))
Esempio n. 3
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))