def get_total_degree_polynomials(univariate_variables, degrees):
    assert type(univariate_variables[0]) == list
    assert len(univariate_variables) == len(degrees)
    polys, nparams = [], []
    for ii in range(len(degrees)):
        poly = PolynomialChaosExpansion()
        var_trans = AffineRandomVariableTransformation(
            univariate_variables[ii])
        poly_opts = define_poly_options_from_variable_transformation(var_trans)
        poly.configure(poly_opts)
        indices = compute_hyperbolic_indices(var_trans.num_vars(), degrees[ii],
                                             1.0)
        poly.set_indices(indices)
        polys.append(poly)
        nparams.append(indices.shape[1])
    return polys, np.array(nparams)
    def test_hermite_basis_for_lognormal_variables(self):
        def function(x):
            return (x.T)**2

        degree = 2
        # mu_g, sigma_g = 1e1, 0.1
        mu_l, sigma_l = 2.1e11, 2.1e10
        mu_g = np.log(mu_l**2 / np.sqrt(mu_l**2 + sigma_l**2))
        sigma_g = np.sqrt(np.log(1 + sigma_l**2 / mu_l**2))

        lognorm = stats.lognorm(s=sigma_g, scale=np.exp(mu_g))
        # assert np.allclose([lognorm.mean(), lognorm.std()], [mu_l, sigma_l])

        univariate_variables = [stats.norm(mu_g, sigma_g)]
        var_trans = AffineRandomVariableTransformation(univariate_variables)
        pce = PolynomialChaosExpansion()
        pce_opts = define_poly_options_from_variable_transformation(var_trans)
        pce.configure(pce_opts)
        pce.set_indices(
            compute_hyperbolic_indices(var_trans.num_vars(), degree, 1.))

        nsamples = int(1e6)
        samples = lognorm.rvs(nsamples)[None, :]
        values = function(samples)

        ntrain_samples = 20
        train_samples = lognorm.rvs(ntrain_samples)[None, :]
        train_values = function(train_samples)
        from pyapprox.quantile_regression import solve_quantile_regression, \
            solve_least_squares_regression
        coef = solve_quantile_regression(0.5,
                                         np.log(train_samples),
                                         train_values,
                                         pce.basis_matrix,
                                         normalize_vals=True)
        pce.set_coefficients(coef)
        print(pce.mean(), values.mean())
        assert np.allclose(pce.mean(), values.mean(), rtol=1e-3)
Beispiel #3
0
def adaptive_approximate_polynomial_chaos(
        fun,
        univariate_variables,
        callback=None,
        refinement_indicator=variance_pce_refinement_indicator,
        growth_rules=None,
        max_nsamples=100,
        tol=0,
        verbose=0,
        ncandidate_samples=1e4,
        generate_candidate_samples=None):
    r"""
    Compute an adaptive Polynomial Chaos Expansion of a function.

    Parameters
    ----------
    fun : callable
        The function to be minimized

        ``fun(z) -> np.ndarray``

        where ``z`` is a 2D np.ndarray with shape (nvars,nsamples) and the
        output is a 2D np.ndarray with shape (nsamples,nqoi)

    univariate_variables : list
        A list of scipy.stats random variables of size (nvars)

    callback : callable
        Function called after each iteration with the signature
        
        ``callback(approx_k)``

        where approx_k is the current approximation object.

    refinement_indicator : callable
        A function that retuns an estimate of the error of a sparse grid subspace
        with signature
    
        ``refinement_indicator(subspace_index,nnew_subspace_samples,sparse_grid) -> float, float``

        where ``subspace_index`` is 1D np.ndarray of size (nvars), 
        ``nnew_subspace_samples`` is an integer specifying the number
        of new samples that will be added to the sparse grid by adding the 
        subspace specified by subspace_index and ``sparse_grid`` is the current 
        :class:`pyapprox.adaptive_sparse_grid.CombinationSparseGrid` object. 
        The two outputs are, respectively, the indicator used to control 
        refinement of the sparse grid and the change in error from adding the 
        current subspace. The indicator is typically but now always dependent on 
        the error.

    growth_rules : list or callable
        a list (or single callable) of growth rules with signature

        ``growth_rule(l)->integer``

        where the output ``nsamples`` specifies the number of indices of the 
        univariate basis of level ``l``.

        If the entry is a callable then the same growth rule is 
        applied to every variable.

    max_nsamples : integer
        The maximum number of evaluations of fun.

    tol : float
        Tolerance for termination. The construction of the sparse grid is 
        terminated when the estimate error in the sparse grid (determined by 
        ``refinement_indicator`` is below tol.

    verbose : integer
        Controls messages printed during construction.

    ncandidate_samples : integer
        The number of candidate samples used to generate the Leja sequence
        The Leja sequence will be a subset of these samples.

    generate_candidate_samples : callable
        A function that generates the candidate samples used to build the Leja
        sequence with signature

        ``generate_candidate_samples(ncandidate_samples) -> np.ndarray``
    
        The output is a 2D np.ndarray with size(nvars,ncandidate_samples)

    Returns
    -------
    pce : :class:`pyapprox.multivariate_polynomials.PolynomialChaosExpansion`
        The PCE approximation
    """
    variable = IndependentMultivariateRandomVariable(univariate_variables)
    var_trans = AffineRandomVariableTransformation(variable)
    nvars = var_trans.num_vars()

    bounded_variables = True
    for rv in univariate_variables:
        if not is_bounded_continuous_variable(rv):
            bounded_variables = False
            msg = "For now leja sampling based PCE is only supported for bounded continouous random variablesfor now leja sampling based PCE is only supported for bounded continouous random variables"
            if generate_candidate_samples is None:
                raise Exception(msg)
            else:
                break
    if generate_candidate_samples is None:
        # Todo implement default for non-bounded variables that uses induced
        # sampling
        # candidate samples must be in canonical domain
        from pyapprox import halton_sequence
        candidate_samples = -np.cos(
            np.pi * halton_sequence(nvars, 1, int(ncandidate_samples + 1)))
        #candidate_samples = -np.cos(
        #    np.random.uniform(0,np.pi,(nvars,int(ncandidate_samples))))
    else:
        candidate_samples = generate_candidate_samples(ncandidate_samples)

    pce = AdaptiveLejaPCE(nvars, candidate_samples, factorization_type='fast')
    pce.verbose = verbose
    admissibility_function = partial(max_level_admissibility_function,
                                     np.inf, [np.inf] * nvars,
                                     max_nsamples,
                                     tol,
                                     verbose=verbose)
    pce.set_function(fun, var_trans)
    if growth_rules is None:
        growth_rules = clenshaw_curtis_rule_growth
    pce.set_refinement_functions(refinement_indicator, admissibility_function,
                                 growth_rules)
    pce.build(callback)
    return pce
Beispiel #4
0
def adaptive_approximate_sparse_grid(
        fun,
        univariate_variables,
        callback=None,
        refinement_indicator=variance_refinement_indicator,
        univariate_quad_rule_info=None,
        max_nsamples=100,
        tol=0,
        verbose=0,
        config_variables_idx=None,
        config_var_trans=None,
        cost_function=None,
        max_level_1d=None):
    """
    Compute a sparse grid approximation of a function.

    Parameters
    ----------
    fun : callable
        The function to be minimized

        ``fun(z) -> np.ndarray``

        where ``z`` is a 2D np.ndarray with shape (nvars,nsamples) and the
        output is a 2D np.ndarray with shape (nsamples,nqoi)

    univariate_variables : list
        A list of scipy.stats random variables of size (nvars)

    callback : callable
        Function called after each iteration with the signature
        
        ``callback(approx_k)``

        where approx_k is the current approximation object.

    refinement_indicator : callable
        A function that retuns an estimate of the error of a sparse grid subspace
        with signature
    
        ``refinement_indicator(subspace_index,nnew_subspace_samples,sparse_grid) -> float, float``

        where ``subspace_index`` is 1D np.ndarray of size (nvars), 
        ``nnew_subspace_samples`` is an integer specifying the number
        of new samples that will be added to the sparse grid by adding the 
        subspace specified by subspace_index and ``sparse_grid`` is the current 
        :class:`pyapprox.adaptive_sparse_grid.CombinationSparseGrid` object. 
        The two outputs are, respectively, the indicator used to control 
        refinement of the sparse grid and the change in error from adding the 
        current subspace. The indicator is typically but now always dependent on 
        the error.

    univariate_quad_rule_info : list
        List containing two entries. The first entry is a list 
        (or single callable) of univariate quadrature rules for each variable
        with signature

        ``quad_rule(l)->np.ndarray,np.ndarray``

        where the integer ``l`` specifies the level of the quadrature rule and 
        ``x`` and ``w`` are 1D np.ndarray of size (nsamples) containing the 
        quadrature rule points and weights, respectively.

        The second entry is a list (or single callable) of growth rules
        with signature

        ``growth_rule(l)->integer``

        where the output ``nsamples`` specifies the number of samples in the 
        quadrature rule of level ``l``.

        If either entry is a callable then the same quad or growth rule is 
        applied to every variable.

    max_nsamples : float
        If ``cost_function==None`` then this argument is the maximum number of 
        evaluations of fun. If fun has configure variables

        If ``cost_function!=None`` Then max_nsamples is the maximum cost of 
        constructing the sparse grid, i.e. the sum of the cost of evaluating
        each point in the sparse grid.

        The ``cost_function!=None` same behavior as ``cost_function==None``
        can be obtained by setting cost_function = lambda config_sample: 1.

        This is particularly useful if ``fun`` has configure variables
        and evaluating ``fun`` at these different values of these configure
        variables has different cost. For example if there is one configure
        variable that can take two different values with cost 0.5, and 1
        then 10 samples of both models will be measured as 15 samples and
        so if max_nsamples is 19 the algorithm will not terminate because
        even though the number of samples is the sparse grid is 20.

    tol : float
        Tolerance for termination. The construction of the sparse grid is 
        terminated when the estimate error in the sparse grid (determined by 
        ``refinement_indicator`` is below tol.

    verbose : integer
        Controls messages printed during construction.

    config_variable_idx : integer
        The position in a sample array that the configure variables start

    config_var_trans : pyapprox.adaptive_sparse_grid.ConfigureVariableTransformation
        An object that takes configure indices in [0,1,2,3...] 
        and maps them to the configure values accepted by ``fun``

    cost_function : callable 
        A function with signature

        ``cost_function(config_sample) -> float``
    
        where config_sample is a np.ndarray of shape (nconfig_vars). The output
        is the cost of evaluating ``fun`` at ``config_sample``. The units can be
        anything but the units must be consistent with the units of max_nsamples
        which specifies the maximum cost of constructing the sparse grid.

    max_level_1d : np.ndarray (nvars)
        The maximum level of the sparse grid in each dimension. If None
        There is no limit

    Returns
    -------
    sparse_grid : :class:`pyapprox.adaptive_sparse_grid.CombinationSparseGrid`
        The sparse grid approximation
    """
    variable = IndependentMultivariateRandomVariable(univariate_variables)
    var_trans = AffineRandomVariableTransformation(variable)
    nvars = var_trans.num_vars()
    if config_var_trans is not None:
        nvars += config_var_trans.num_vars()
    sparse_grid = CombinationSparseGrid(nvars)
    if univariate_quad_rule_info is None:
        quad_rules, growth_rules, unique_quadrule_indices = \
            get_sparse_grid_univariate_leja_quadrature_rules_economical(
            var_trans)
    else:
        quad_rules, growth_rules = univariate_quad_rule_info
        unique_quadrule_indices = None
    if max_level_1d is None:
        max_level_1d = [np.inf] * nvars
    assert len(max_level_1d) == nvars
    admissibility_function = partial(max_level_admissibility_function,
                                     np.inf,
                                     max_level_1d,
                                     max_nsamples,
                                     tol,
                                     verbose=verbose)
    sparse_grid.setup(fun,
                      config_variables_idx,
                      refinement_indicator,
                      admissibility_function,
                      growth_rules,
                      quad_rules,
                      var_trans,
                      unique_quadrule_indices=unique_quadrule_indices,
                      verbose=verbose,
                      cost_function=cost_function,
                      config_var_trans=config_var_trans)
    sparse_grid.build(callback)
    return sparse_grid
def genz_example(max_num_samples, precond_type):
    error_tol = 1e-12

    univariate_variables = [uniform(), beta(3, 3)]
    variable = IndependentMultivariateRandomVariable(univariate_variables)
    var_trans = AffineRandomVariableTransformation(variable)

    c = np.array([10, 0.00])
    model = GenzFunction("oscillatory",
                         variable.num_vars(),
                         c=c,
                         w=np.zeros_like(c))
    # model.set_coefficients(4,'exponential-decay')

    validation_samples = generate_independent_random_samples(
        var_trans.variable, int(1e3))
    validation_values = model(validation_samples)

    errors = []
    num_samples = []

    def callback(pce):
        error = compute_l2_error(validation_samples, validation_values, pce)
        errors.append(error)
        num_samples.append(pce.samples.shape[1])

    candidate_samples = -np.cos(
        np.random.uniform(0, np.pi, (var_trans.num_vars(), int(1e4))))
    pce = AdaptiveLejaPCE(var_trans.num_vars(),
                          candidate_samples,
                          factorization_type='fast')
    if precond_type == 'density':

        def precond_function(basis_matrix, samples):
            trans_samples = var_trans.map_from_canonical_space(samples)
            vals = np.ones(samples.shape[1])
            for ii in range(len(univariate_variables)):
                rv = univariate_variables[ii]
                vals *= np.sqrt(rv.pdf(trans_samples[ii, :]))
            return vals
    elif precond_type == 'christoffel':
        precond_function = chistoffel_preconditioning_function
    else:
        raise Exception(f'Preconditioner: {precond_type} not supported')
    pce.set_preconditioning_function(precond_function)

    max_level = np.inf
    max_level_1d = [max_level] * (pce.num_vars)

    admissibility_function = partial(max_level_admissibility_function,
                                     max_level, max_level_1d, max_num_samples,
                                     error_tol)

    growth_rule = partial(constant_increment_growth_rule, 2)
    #growth_rule = clenshaw_curtis_rule_growth
    pce.set_function(model, var_trans)
    pce.set_refinement_functions(variance_pce_refinement_indicator,
                                 admissibility_function, growth_rule)

    while (not pce.active_subspace_queue.empty()
           or pce.subspace_indices.shape[1] == 0):
        pce.refine()
        pce.recompute_active_subspace_priorities()
        if callback is not None:
            callback(pce)

    from pyapprox.sparse_grid import plot_sparse_grid_2d
    plot_sparse_grid_2d(pce.samples, np.ones(pce.samples.shape[1]),
                        pce.pce.indices, pce.subspace_indices)

    plt.figure()
    plt.loglog(num_samples, errors, 'o-')
    plt.show()
Beispiel #6
0
def approximate_sparse_grid(fun,
                            univariate_variables,
                            callback=None,
                            refinement_indicator=variance_refinement_indicator,
                            univariate_quad_rule_info=None,
                            max_nsamples=100,
                            tol=0,
                            verbose=False):
    """
    Compute a sparse grid approximation of a function.

    Parameters
    ----------
    fun : callable
        The function to be minimized

        ``fun(z) -> np.ndarray``

        where ``z`` is a 2D np.ndarray with shape (nvars,nsamples) and the
        output is a 2D np.ndarray with shaoe (nsamples,nqoi)

    univariate_variables : list
        A list of scipy.stats random variables of size (nvars)

    callback : callable
        Function called after each iteration with the signature
        
        ``callback(approx_k)``

        where approx_k is the current approximation object.

    refinement_indicator : callable
        A function that retuns an estimate of the error of a sparse grid subspace
        with signature
    
        ``refinement_indicator(subspace_index,nnew_subspace_samples,sparse_grid) -> float, float``

        where ``subspace_index`` is 1D np.ndarray of size (nvars), 
        ``nnew_subspace_samples`` is an integer specifying the number
        of new samples that will be added to the sparse grid by adding the 
        subspace specified by subspace_index and ``sparse_grid`` is the current 
        :class:`pyapprox.adaptive_sparse_grid.CombinationSparseGrid` object. 
        The two outputs are, respectively, the indicator used to control 
        refinement of the sparse grid and the change in error from adding the 
        current subspace. The indicator is typically but now always dependent on 
        the error.

    univariate_quad_rule_info : list
        List containing two entries. The first entry is a list 
        (or single callable) of univariate quadrature rules for each variable
        with signature

        ``quad_rule(l)->np.ndarray,np.ndarray``

        where the integer ``l`` specifies the level of the quadrature rule and 
        ``x`` and ``w`` are 1D np.ndarray of size (nsamples) containing the 
        quadrature rule points and weights, respectively.

        The second entry is a list (or single callable) of growth rules
        with signature

        ``growth_rule(l)->integer``

        where the output ``nsamples`` specifies the number of samples in the 
        quadrature rule of level``l``.

        If either entry is a callable then the same quad or growth rule is 
        applied to every variable.

    max_nsamples : integer
        The maximum number of evaluations of fun.

    tol : float
        Tolerance for termination. The construction of the sparse grid is 
        terminated when the estimate error in the sparse grid (determined by 
        ``refinement_indicator`` is below tol.

    verbose: boolean
        Controls messages printed during construction.

    Returns
    -------
    sparse_grid : :class:`pyapprox.adaptive_sparse_grid.CombinationSparseGrid`
        The sparse grid approximation
    """
    variable = IndependentMultivariateRandomVariable(univariate_variables)
    var_trans = AffineRandomVariableTransformation(variable)
    nvars = var_trans.num_vars()
    sparse_grid = CombinationSparseGrid(nvars)
    if univariate_quad_rule_info is None:
        quad_rules, growth_rules, unique_quadrule_indices = \
            get_sparse_grid_univariate_leja_quadrature_rules_economical(
                var_trans)
    else:
        quad_rules, growth_rules = univariate_quad_rule_info
        unique_quadrule_indices = None
    admissibility_function = partial(max_level_admissibility_function,
                                     np.inf, [np.inf] * nvars,
                                     max_nsamples,
                                     tol,
                                     verbose=verbose)
    sparse_grid.setup(fun,
                      None,
                      variance_refinement_indicator,
                      admissibility_function,
                      growth_rules,
                      quad_rules,
                      var_trans,
                      unique_quadrule_indices=unique_quadrule_indices)
    sparse_grid.build(callback)
    return sparse_grid