示例#1
0
def get_candidate_based_leja_sequence_1d(num_leja_samples,
                                         recursion_coeffs,
                                         generate_candidate_samples,
                                         num_candidate_samples,
                                         weight_function,
                                         initial_points=None,
                                         samples_filename=None):

    from pyapprox.orthonormal_polynomials_1d import \
        evaluate_orthonormal_polynomial_1d
    from pyapprox.polynomial_sampling import get_lu_leja_samples
    generate_basis_matrix = lambda x: evaluate_orthonormal_polynomial_1d(
        x[0, :], num_leja_samples, recursion_coeffs)
    if samples_filename is None or not os.path.exists(samples_filename):
        leja_sequence, __ = get_lu_leja_samples(
            generate_basis_matrix,
            generate_candidate_samples,
            num_candidate_samples,
            num_leja_samples,
            preconditioning_function=weight_function,
            initial_samples=initial_points)
        if samples_filename is not None:
            np.savez(samples_filename, samples=leja_sequence)
    else:
        leja_sequence = np.load(samples_filename)['samples']
        assert leja_sequence.shape[1] >= num_leja_samples
        leja_sequence = leja_sequence[:, :num_leja_samples]

    return leja_sequence
示例#2
0
def demo_idist_jacobi():
    alph = -0.8
    bet = np.sqrt(101)
    #n = 8;    M = 1001
    n = 4
    M = 5

    x = np.cos(np.linspace(0, np.pi, M + 2))[:, np.newaxis]
    x = x[1:-1]

    F = idist_jacobi(x, n, alph, bet)

    recursion_coeffs = jacobi_recurrence(n + 1, alph, bet, True)
    #recursion_coeffs[:,1]=np.sqrt(recursion_coeffs[:,1])
    polys = evaluate_orthonormal_polynomial_1d(x[:, 0], n, recursion_coeffs)
    wt_function = (1 - x)**alph * (1 + x)**bet / (2**(alph + bet + 1) *
                                                  betafn(bet + 1, alph + 1))
    f = polys[:, -1:]**2 * wt_function

    fig, ax = plt.subplots(1, 1)
    plt.plot(x, f)
    ax.set_xlabel('$x$')
    ax.set_ylabel('$p_n^2(x) \mathrm{d}\mu(x)$')
    ax.set_xlim(-1, 1)
    ax.set_ylim(0, 4)

    fig, ax = plt.subplots(1, 1)
    plt.plot(x, F)
    ax.set_xlabel('$x$')
    ax.set_ylabel('$F_n(x)$')
    ax.set_xlim(-1, 1)
    plt.show()
示例#3
0
def compute_univariate_orthonormal_basis_products(get_recursion_coefficients,
                                                  max_degree1, max_degree2):
    """
    Compute all the products of univariate orthonormal bases and re-express 
    them as expansions using the orthnormal basis.
    """
    assert max_degree1 >= max_degree2
    max_degree = max_degree1 + max_degree2
    num_quad_points = max_degree + 1

    recursion_coefs = get_recursion_coefficients(num_quad_points)
    x_quad, w_quad = gauss_quadrature(recursion_coefs, num_quad_points)
    w_quad = w_quad[:, np.newaxis]

    # evaluate the orthonormal basis at the quadrature points. This can
    # be computed once for all degrees up to the maximum degree
    ortho_basis_matrix = evaluate_orthonormal_polynomial_1d(
        x_quad, max_degree, recursion_coefs)

    # compute coefficients of orthonormal basis using pseudo
    # spectral projection
    product_coefs = []
    for d1 in range(max_degree1 + 1):
        for d2 in range(min(d1 + 1, max_degree2 + 1)):
            product_vals = ortho_basis_matrix[:, d1] * ortho_basis_matrix[:,
                                                                          d2]
            coefs = w_quad.T.dot(product_vals[:, np.newaxis] *
                                 ortho_basis_matrix[:, :d1 + d2 + 1]).T
            product_coefs.append(coefs)
    return product_coefs
 def integrand(x):
     pvals = evaluate_orthonormal_polynomial_1d(x, ii, ab)
     #from matplotlib import pyplot as plt
     #print(ab[0, 1])
     #plt.plot(x, pvals[:, 1])
     #plt.plot(x, x/ab[0, 1]**2)
     #plt.show()
     return measure(x) * pvals[:, ii] * pvals[:, ii - 1]
示例#5
0
def float_rv_discrete_inverse_transform_sampling_1d(xk, pk, ab, ii, u_samples):
    poly_vals = evaluate_orthonormal_polynomial_1d(np.asarray(xk, dtype=float),
                                                   ii, ab)[:, -1]
    probability_masses = pk * poly_vals**2
    cdf_vals = np.cumsum(probability_masses)
    assert np.allclose(cdf_vals[-1], 1), cdf_vals[-1]
    #cdf_vals/=cdf_vals[-1]
    sample_indices = np.searchsorted(cdf_vals, u_samples)
    samples = xk[sample_indices]
    return samples
示例#6
0
def convert_univariate_lagrange_basis_to_orthonormal_polynomials(
        samples_1d, get_recursion_coefficients):
    """
    Returns
    -------
    coeffs_1d : list [np.ndarray(num_terms_i,num_terms_i)]
        The coefficients of the orthonormal polynomial representation of
        each Lagrange basis. The columns are the coefficients of each 
        lagrange basis. The rows are the coefficient of the degree i 
        orthonormalbasis
    """
    # Get the maximum number of terms in the orthonormal polynomial that
    # are need to interpolate all the interpolation nodes in samples_1d
    max_num_terms = samples_1d[-1].shape[0]
    num_quad_points = max_num_terms + 1
    # Get the recursion coefficients of the orthonormal basis
    recursion_coeffs = get_recursion_coefficients(num_quad_points)
    # compute the points and weights of the correct quadrature rule
    x_quad, w_quad = gauss_quadrature(recursion_coeffs, num_quad_points)
    # evaluate the orthonormal basis at the quadrature points. This can
    # be computed once for all degrees up to the maximum degree
    ortho_basis_matrix = evaluate_orthonormal_polynomial_1d(
        x_quad, max_num_terms, recursion_coeffs)

    # compute coefficients of orthonormal basis using pseudo spectral projection
    coeffs_1d = []
    w_quad = w_quad[:, np.newaxis]
    for ll in range(len(samples_1d)):
        num_terms = samples_1d[ll].shape[0]
        # evaluate the lagrange basis at the quadrature points
        barycentric_weights_1d = [
            compute_barycentric_weights_1d(samples_1d[ll])
        ]
        values = np.eye((num_terms), dtype=float)
        # Sometimes the following function will cause the erro
        # interpolation abscissa are not unique. This can be due to x_quad
        # not abscissa. E.g. x_quad may have points far enough outside
        # range of abscissa, e.g. abscissa are clenshaw curtis points and
        # x_quad points are Gauss-Hermite quadrature points
        lagrange_basis_vals = multivariate_barycentric_lagrange_interpolation(
            x_quad[np.newaxis, :], samples_1d[ll][np.newaxis, :],
            barycentric_weights_1d, values, np.zeros(1, dtype=int))
        # compute fourier like coefficients
        basis_coeffs = []
        for ii in range(num_terms):
            basis_coeffs.append(
                np.dot(w_quad.T, lagrange_basis_vals *
                       ortho_basis_matrix[:, ii:ii + 1])[0, :])
        coeffs_1d.append(np.asarray(basis_coeffs))
    return coeffs_1d
示例#7
0
def continuous_induced_measure_cdf(pdf, ab, ii, lb, ub, tol, x):
    x = np.atleast_1d(x)
    assert x.ndim == 1
    assert x.min() >= lb and x.max() <= ub
    integrand = lambda xx: evaluate_orthonormal_polynomial_1d(
        np.atleast_1d(xx), ii, ab)[:, -1]**2 * pdf(xx)
    #from pyapprox.cython.orthonormal_polynomials_1d import\
    #    induced_measure_pyx
    #integrand = lambda x: induced_measure_pyx(x,ii,ab,pdf)
    vals = np.empty_like(x, dtype=float)
    for jj in range(x.shape[0]):
        integral, err = integrate.quad(integrand,
                                       lb,
                                       x[jj],
                                       epsrel=tol,
                                       epsabs=tol,
                                       limit=100)
        vals[jj] = integral
        # avoid numerical issues at boundary of domain
        if vals[jj] > 1 and vals[jj] - 1 < tol:
            vals[jj] = 1.
    return vals
示例#8
0
def candidate_based_leja_rule(recursion_coeffs,
                              generate_candidate_samples,
                              num_candidate_samples,
                              level,
                              initial_points=None,
                              growth_rule=leja_growth_rule,
                              samples_filename=None,
                              return_weights_for_all_levels=True):
    from pyapprox.orthonormal_polynomials_1d import \
        evaluate_orthonormal_polynomial_1d
    from pyapprox.polynomial_sampling import get_lu_leja_samples,\
        christoffel_preconditioner, christoffel_weights
    num_leja_samples = growth_rule(level)
    generate_basis_matrix = lambda x: evaluate_orthonormal_polynomial_1d(
        x[0, :], num_leja_samples, recursion_coeffs)
    if samples_filename is None or not os.path.exists(samples_filename):
        leja_sequence, __ = get_lu_leja_samples(
            generate_basis_matrix,
            generate_candidate_samples,
            num_candidate_samples,
            num_leja_samples,
            preconditioning_function=christoffel_preconditioner,
            initial_samples=initial_points)
        if samples_filename is not None:
            np.savez(samples_filename, samples=leja_sequence)
    else:
        leja_sequence = np.load(samples_filename)['samples']
        assert leja_sequence.shape[1] >= growth_rule(level)
        leja_sequence = leja_sequence[:, :growth_rule(level)]

    weight_function = lambda x: christoffel_weights(generate_basis_matrix(x))
    ordered_weights_1d = get_leja_sequence_quadrature_weights(
        leja_sequence, growth_rule, generate_basis_matrix, weight_function,
        level, return_weights_for_all_levels)

    return leja_sequence[0, :], ordered_weights_1d
示例#9
0
def basis_matrix_generator_1d(pce, nmax, dd, samples):
    vals = evaluate_orthonormal_polynomial_1d(
        samples, nmax, pce.recursion_coeffs[pce.basis_type_index_map[dd]])
    return vals
示例#10
0
def conditional_moments_of_polynomial_chaos_expansion(poly,
                                                      samples,
                                                      inactive_idx,
                                                      return_variance=False):
    """
    Return mean and variance of polynomial chaos expansion with some variables
    fixed at specified values.

    Parameters
    ----------
    poly: PolynomialChaosExpansion
        The polynomial used to compute moments

    inactive_idx : np.ndarray (ninactive_vars)
        The indices of the fixed variables

    samples : np.ndarray (ninactive_vars)
        The samples of the inacive dimensions fixed when computing moments

    Returns
    -------
    mean : np.ndarray
       The conditional mean (num_qoi)

    variance : np.ndarray
       The conditional variance (num_qoi). Only returned if 
       return_variance=True. Computing variance is significantly slower than
       computing mean. TODO check it is indeed slower
    """
    assert samples.shape[0] == len(inactive_idx)
    assert samples.ndim == 2 and samples.shape[1] == 1
    assert poly.coefficients is not None
    coef = poly.get_coefficients()
    indices = poly.get_indices()

    # precompute 1D basis functions for faster evaluation of
    # multivariate terms
    basis_vals_1d = []
    for dd in range(len(inactive_idx)):
        basis_vals_1d_dd = evaluate_orthonormal_polynomial_1d(
            samples[dd, :], indices[inactive_idx[dd], :].max(),
            poly.recursion_coeffs[poly.basis_type_index_map[inactive_idx[dd]]])
        basis_vals_1d.append(basis_vals_1d_dd)

    active_idx = np.setdiff1d(np.arange(poly.num_vars()), inactive_idx)
    mean = coef[0].copy()
    for ii in range(1, indices.shape[1]):
        index = indices[:, ii]
        coef_ii = coef[ii]  # this intentionally updates the coef matrix
        for dd in range(len(inactive_idx)):
            coef_ii *= basis_vals_1d[dd][0, index[inactive_idx[dd]]]
        if index[active_idx].sum() == 0:
            mean += coef_ii

    if not return_variance:
        return mean

    unique_indices, repeated_idx = np.unique(indices[active_idx, :],
                                             axis=1,
                                             return_inverse=True)
    new_coef = np.zeros((unique_indices.shape[1], coef.shape[1]))
    for ii in range(repeated_idx.shape[0]):
        new_coef[repeated_idx[ii]] += coef[ii]
    variance = np.sum(new_coef**2, axis=0) - mean**2
    return mean, variance
示例#11
0
 def get_recursion_coefficients(self, opts, num_coefs):
     poly_type = opts.get('poly_type', None)
     var_type = None
     if poly_type is None:
         var_type = opts['rv_type']
     if poly_type == 'legendre' or var_type == 'uniform':
         recursion_coeffs = jacobi_recurrence(num_coefs,
                                              alpha=0,
                                              beta=0,
                                              probability=True)
     elif poly_type == 'jacobi' or var_type == 'beta':
         if poly_type is not None:
             alpha_poly, beta_poly = opts['alpha_poly'], opts['beta_poly']
         else:
             alpha_poly, beta_poly = opts['shapes']['b'] - 1, opts[
                 'shapes']['a'] - 1
         recursion_coeffs = jacobi_recurrence(num_coefs,
                                              alpha=alpha_poly,
                                              beta=beta_poly,
                                              probability=True)
     elif poly_type == 'hermite' or var_type == 'norm':
         recursion_coeffs = hermite_recurrence(num_coefs,
                                               rho=0.,
                                               probability=True)
     elif poly_type == 'krawtchouk' or var_type == 'binom':
         if poly_type is None:
             opts = opts['shapes']
         n, p = opts['n'], opts['p']
         num_coefs = min(num_coefs, n)
         recursion_coeffs = krawtchouk_recurrence(num_coefs, n, p)
     elif poly_type == 'hahn' or var_type == 'hypergeom':
         if poly_type is not None:
             apoly, bpoly = opts['alpha_poly'], opts['beta_poly']
             N = opts['N']
         else:
             M, n, N = [opts['shapes'][key] for key in ['M', 'n', 'N']]
             apoly, bpoly = -(n + 1), -M - 1 + n
         num_coefs = min(num_coefs, N)
         recursion_coeffs = hahn_recurrence(num_coefs, N, apoly, bpoly)
     elif poly_type == 'discrete_chebyshev' or var_type == 'discrete_chebyshev':
         if poly_type is not None:
             N = opts['N']
         else:
             N = opts['shapes']['xk'].shape[0]
             assert np.allclose(opts['shapes']['xk'], np.arange(N))
             assert np.allclose(opts['shapes']['pk'], np.ones(N) / N)
         num_coefs = min(num_coefs, N)
         recursion_coeffs = discrete_chebyshev_recurrence(num_coefs, N)
     elif poly_type == 'discrete_numeric' or var_type == 'float_rv_discrete':
         if poly_type is None:
             opts = opts['shapes']
         xk, pk = opts['xk'], opts['pk']
         #shapes['xk'] will be in [0,1] but canonical domain is [-1,1]
         xk = xk * 2 - 1
         assert xk.min() >= -1 and xk.max() <= 1
         if num_coefs > xk.shape[0]:
             msg = 'Number of coefs requested is larger than number of '
             msg += 'probability masses'
             raise Exception(msg)
         recursion_coeffs = modified_chebyshev_orthonormal(
             num_coefs, [xk, pk])
         p = evaluate_orthonormal_polynomial_1d(np.asarray(xk, dtype=float),
                                                num_coefs - 1,
                                                recursion_coeffs)
         error = np.absolute((p.T * pk).dot(p) - np.eye(num_coefs)).max()
         if error > self.numerically_generated_poly_accuracy_tolerance:
             msg = f'basis created is ill conditioned. '
             msg += f'Max error: {error}. Max terms: {xk.shape[0]}, '
             msg += f'Terms requested: {num_coefs}'
             raise Exception(msg)
     elif poly_type == 'monomial':
         recursion_coeffs = None
     else:
         if poly_type is not None:
             raise Exception('poly_type (%s) not supported' % poly_type)
         else:
             raise Exception('var_type (%s) not supported' % var_type)
     return recursion_coeffs
示例#12
0
def get_recursion_coefficients(
        opts,
        num_coefs,
        numerically_generated_poly_accuracy_tolerance=1e-12):
    """
    Parameters
    ----------
    num_coefs : interger
        The number of recursion coefficients desired

    numerically_generated_poly_accuracy_tolerance : float
            Tolerance used to construct any numerically generated polynomial
            basis functions.

    opts : dictionary
        Dictionary with the following attributes

    rv_type : string
        The type of variable associated with the polynomial. If poly_type
        is not provided then the recursion coefficients chosen is selected
        using the Askey scheme. E.g. uniform -> legendre, norm -> hermite.
        rv_type is assumed to be the name of the distribution of scipy.stats
        variables, e.g. for gaussian rv_type = norm(0, 1).dist

    poly_type : string
        The type of polynomial which overides rv_type. Supported types
        ['legendre', 'hermite', 'jacobi', 'krawtchouk', 'hahn',
        'discrete_chebyshev', 'discrete_numeric', 'continuous_numeric',
        'function_indpnt_vars', 'product_indpnt_vars', 'monomial']
        Note 'monomial' does not produce an orthogonal basis

    The remaining options are specific to rv_type and poly_type. See
     - :func:`pyapprox.univariate_quadrature.get_jacobi_recursion_coefficients`
     - :func:`pyapprox.univariate_quadrature.get_function_independent_vars_recursion_coefficients`
     - :func:`pyapprox.univariate_quadrature.get_product_independent_vars_recursion_coefficients`
    
        Note Legendre is just a special instance of a Jacobi polynomial with
        alpha_poly, beta_poly = 0, 0 and alpha_stat, beta_stat = 1, 1

    Returns
    -------
    recursion_coeffs : np.ndarray (num_coefs, 2)
    """

    # variables that require numerically generated polynomials with
    # predictor corrector method
    from scipy import stats
    from scipy.stats import _continuous_distns

    poly_type = opts.get('poly_type', None)
    var_type = None
    if poly_type is None:
        var_type = opts['rv_type']
    if poly_type == 'legendre' or var_type == 'uniform':
        recursion_coeffs = jacobi_recurrence(
            num_coefs, alpha=0, beta=0, probability=True)
    elif poly_type == 'jacobi' or var_type == 'beta':
        recursion_coeffs = get_jacobi_recursion_coefficients(
            poly_type, opts, num_coefs)
    elif poly_type == 'hermite' or var_type == 'norm':
        recursion_coeffs = hermite_recurrence(
            num_coefs, rho=0., probability=True)
    elif poly_type == 'krawtchouk' or var_type == 'binom':
        # although bounded the krwatchouk polynomials are not defined
        # on the canonical domain [-1,1] but rather the user and
        # canconical domain are the same
        if poly_type is None:
            opts = opts['shapes']
        n, p = opts['n'], opts['p']
        num_coefs = min(num_coefs, n)
        recursion_coeffs = krawtchouk_recurrence(
            num_coefs, n, p)
    elif poly_type == 'hahn' or var_type == 'hypergeom':
        # although bounded the hahn polynomials are not defined
        # on the canonical domain [-1,1] but rather the user and
        # canconical domain are the same
        if poly_type is not None:
            apoly, bpoly = opts['alpha_poly'], opts['beta_poly']
            N = opts['N']
        else:
            M, n, N = [opts['shapes'][key] for key in ['M', 'n', 'N']]
            apoly, bpoly = -(n+1), -M-1+n
        num_coefs = min(num_coefs, N)
        recursion_coeffs = hahn_recurrence(num_coefs, N, apoly, bpoly)
        xk = np.arange(max(0, N-M+n), min(n, N)+1, dtype=float)
    elif poly_type == 'discrete_chebyshev' or var_type == 'discrete_chebyshev':
        # although bounded the discrete_chebyshev polynomials are not defined
        # on the canonical domain [-1,1] but rather the user and
        # canconical domain are the same
        if poly_type is not None:
            N = opts['N']
        else:
            N = opts['shapes']['xk'].shape[0]
            assert np.allclose(opts['shapes']['xk'], np.arange(N))
            assert np.allclose(opts['shapes']['pk'], np.ones(N)/N)
        num_coefs = min(num_coefs, N)
        recursion_coeffs = discrete_chebyshev_recurrence(num_coefs, N)
    elif poly_type == 'discrete_numeric' or var_type == 'float_rv_discrete':
        if poly_type is None:
            opts = opts['shapes']
        xk, pk = opts['xk'], opts['pk']
        # shapes['xk'] will be in [0, 1] but canonical domain is [-1, 1]
        xk = xk*2-1
        assert xk.min() >= -1 and xk.max() <= 1
        if num_coefs > xk.shape[0]:
            msg = 'Number of coefs requested is larger than number of '
            msg += 'probability masses'
            raise Exception(msg)
        #recursion_coeffs = modified_chebyshev_orthonormal(num_coefs, [xk, pk])
        recursion_coeffs = lanczos(xk, pk, num_coefs)
        p = evaluate_orthonormal_polynomial_1d(
            np.asarray(xk, dtype=float), num_coefs-1, recursion_coeffs)
        error = np.absolute((p.T*pk).dot(p)-np.eye(num_coefs)).max()
        if error > numerically_generated_poly_accuracy_tolerance:
            msg = f'basis created is ill conditioned. '
            msg += f'Max error: {error}. Max terms: {xk.shape[0]}, '
            msg += f'Terms requested: {num_coefs}'
            raise Exception(msg)
    elif (poly_type == 'continuous_numeric' or
          var_type == 'continuous_rv_sample'):
        if poly_type is None:
            opts = opts['shapes']
        xk, pk = opts['xk'], opts['pk']
        if num_coefs > xk.shape[0]:
            msg = 'Number of coefs requested is larger than number of '
            msg += 'samples'
            raise Exception(msg)
        #print(num_coefs)
        #recursion_coeffs = modified_chebyshev_orthonormal(num_coefs, [xk, pk])
        #recursion_coeffs = lanczos(xk, pk, num_coefs)
        recursion_coeffs = predictor_corrector(
            num_coefs, (xk, pk), xk.min(), xk.max(),
            interval_size=xk.max()-xk.min())
        p = evaluate_orthonormal_polynomial_1d(
            np.asarray(xk, dtype=float), num_coefs-1, recursion_coeffs)
        error = np.absolute((p.T*pk).dot(p)-np.eye(num_coefs)).max()
        if error > numerically_generated_poly_accuracy_tolerance:
            msg = f'basis created is ill conditioned. '
            msg += f'Max error: {error}. Max terms: {xk.shape[0]}, '
            msg += f'Terms requested: {num_coefs}'
            raise Exception(msg)
    elif poly_type == 'monomial':
        recursion_coeffs = None
    elif var_type in _continuous_distns._distn_names:
        quad_options = {
            'nquad_samples': 10,
            'atol': numerically_generated_poly_accuracy_tolerance,
            'rtol': numerically_generated_poly_accuracy_tolerance,
            'max_steps': 10000, 'verbose': 0}
        rv = getattr(stats, var_type)(**opts['shapes'])
        recursion_coeffs = predictor_corrector_known_scipy_pdf(
            num_coefs, rv, quad_options)
    elif poly_type == 'function_indpnt_vars':
        recursion_coeffs = get_function_independent_vars_recursion_coefficients(
            opts, num_coefs)
    elif poly_type == 'product_indpnt_vars':
        recursion_coeffs = get_product_independent_vars_recursion_coefficients(
            opts, num_coefs)
    else:
        if poly_type is not None:
            raise Exception('poly_type (%s) not supported' % poly_type)
        else:
            raise Exception('var_type (%s) not supported' % var_type)
    return recursion_coeffs
 def integrand(x):
     y = fun(x).squeeze()
     pvals = evaluate_orthonormal_polynomial_1d(y, ii, ab)
     # measure not included in integral because it is assumed to
     # be in the quadrature rules
     return pvals[:, ii]**2
 def integrand(measure, x):
     # Note eval orthogonal poly uses the new value for ab[ii, 0]
     # This is the desired behavior
     pvals = evaluate_orthonormal_polynomial_1d(x, ii, ab)
     return measure(x) * pvals[:, ii]**2
 def integrand(measure, x):
     pvals = evaluate_orthonormal_polynomial_1d(x, ii, ab)
     return measure(x) * pvals[:, ii] * pvals[:, ii - 1]
示例#16
0
 def integrand(xx): return evaluate_orthonormal_polynomial_1d(
     np.atleast_1d(xx), ii, ab)[:, -1]**2*pdf(xx)
 # from pyapprox.cython.orthonormal_polynomials_1d import\
 #    induced_measure_pyx
 #integrand = lambda x: induced_measure_pyx(x,ii,ab,pdf)
 vals = np.empty_like(x, dtype=float)