def test_lu_leja_interpolation(self): num_vars = 2 degree = 15 poly = PolynomialChaosExpansion() var_trans = define_iid_random_variable_transformation( stats.uniform(), num_vars) opts = define_poly_options_from_variable_transformation(var_trans) poly.configure(opts) indices = compute_hyperbolic_indices(num_vars, degree, 1.0) poly.set_indices(indices) # candidates must be generated in canonical PCE space num_candidate_samples = 10000 def generate_candidate_samples(n): return np.cos( np.random.uniform(0., np.pi, (num_vars, n))) # must use canonical_basis_matrix to generate basis matrix num_leja_samples = indices.shape[1]-1 def precond_func(matrix, samples): return christoffel_weights(matrix) samples, data_structures = get_lu_leja_samples( poly.canonical_basis_matrix, generate_candidate_samples, num_candidate_samples, num_leja_samples, preconditioning_function=precond_func) samples = var_trans.map_from_canonical_space(samples) assert samples.max() <= 1 and samples.min() >= 0. c = np.random.uniform(0., 1., num_vars) c *= 20/c.sum() w = np.zeros_like(c) w[0] = np.random.uniform(0., 1., 1) genz_function = GenzFunction('oscillatory', num_vars, c=c, w=w) values = genz_function(samples) # Ensure coef produce an interpolant coef = interpolate_lu_leja_samples(samples, values, data_structures) # Ignore basis functions (columns) that were not considered during the # incomplete LU factorization poly.set_indices(poly.indices[:, :num_leja_samples]) poly.set_coefficients(coef) assert np.allclose(poly(samples), values) quad_w = get_quadrature_weights_from_lu_leja_samples( samples, data_structures) values_at_quad_x = values[:, 0] # will get closer if degree is increased # print (np.dot(values_at_quad_x,quad_w),genz_function.integrate()) assert np.allclose( np.dot(values_at_quad_x, quad_w), genz_function.integrate(), atol=1e-4)
def test_fekete_rosenblatt_interpolation(self): np.random.seed(2) degree=3 __,__,joint_density,limits = rosenblatt_example_2d(num_samples=1) num_vars=len(limits)//2 rosenblatt_opts = {'limits':limits,'num_quad_samples_1d':20} var_trans_1 = RosenblattTransformation( joint_density,num_vars,rosenblatt_opts) # rosenblatt maps to [0,1] but polynomials of bounded variables # are in [-1,1] so add second transformation for this second mapping var_trans_2 = define_iid_random_variable_transformation( uniform(),num_vars) var_trans = TransformationComposition([var_trans_1, var_trans_2]) poly = PolynomialChaosExpansion() poly.configure({'poly_type':'jacobi','alpha_poly':0., 'beta_poly':0.,'var_trans':var_trans}) indices = compute_hyperbolic_indices(num_vars,degree,1.0) poly.set_indices(indices) num_candidate_samples = 10000 generate_candidate_samples=lambda n: np.cos( np.random.uniform(0.,np.pi,(num_vars,n))) precond_func = lambda matrix, samples: christoffel_weights(matrix) canonical_samples, data_structures = get_fekete_samples( poly.canonical_basis_matrix,generate_candidate_samples, num_candidate_samples,preconditioning_function=precond_func) samples = var_trans.map_from_canonical_space(canonical_samples) assert np.allclose( canonical_samples,var_trans.map_to_canonical_space(samples)) assert samples.max()<=1 and samples.min()>=0. c = np.random.uniform(0.,1.,num_vars) c*=20/c.sum() w = np.zeros_like(c); w[0] = np.random.uniform(0.,1.,1) genz_function = GenzFunction('oscillatory',num_vars,c=c,w=w) values = genz_function(samples) # function = lambda x: np.sum(x**2,axis=0)[:,np.newaxis] # values = function(samples) # Ensure coef produce an interpolant coef = interpolate_fekete_samples( canonical_samples,values,data_structures) poly.set_coefficients(coef) assert np.allclose(poly(samples),values) # compare mean computed using quadrature and mean computed using # first coefficient of expansion. This is not testing that mean # is correct because rosenblatt transformation introduces large error # which makes it hard to compute accurate mean from pce or quadrature quad_w = get_quadrature_weights_from_fekete_samples( canonical_samples,data_structures) values_at_quad_x = values[:,0] assert np.allclose( np.dot(values_at_quad_x,quad_w),poly.mean())
def test_fekete_interpolation(self): num_vars=2 degree=15 poly = PolynomialChaosExpansion() var_trans = define_iid_random_variable_transformation( uniform(),num_vars) opts = define_poly_options_from_variable_transformation(var_trans) poly.configure(opts) indices = compute_hyperbolic_indices(num_vars,degree,1.0) poly.set_indices(indices) # candidates must be generated in canonical PCE space num_candidate_samples = 10000 generate_candidate_samples=lambda n: np.cos( np.random.uniform(0.,np.pi,(num_vars,n))) # must use canonical_basis_matrix to generate basis matrix precond_func = lambda matrix, samples: christoffel_weights(matrix) samples, data_structures = get_fekete_samples( poly.canonical_basis_matrix,generate_candidate_samples, num_candidate_samples,preconditioning_function=precond_func) samples = var_trans.map_from_canonical_space(samples) assert samples.max()<=1 and samples.min()>=0. c = np.random.uniform(0.,1.,num_vars) c*=20/c.sum() w = np.zeros_like(c); w[0] = np.random.uniform(0.,1.,1) genz_function = GenzFunction('oscillatory',num_vars,c=c,w=w) values = genz_function(samples) # Ensure coef produce an interpolant coef = interpolate_fekete_samples(samples,values,data_structures) poly.set_coefficients(coef) assert np.allclose(poly(samples),values) quad_w = get_quadrature_weights_from_fekete_samples( samples,data_structures) values_at_quad_x = values[:,0] # increase degree if want smaller atol assert np.allclose( np.dot(values_at_quad_x,quad_w),genz_function.integrate(), atol=1e-4)
def bivariate_uniform_example(): num_vars = 2 var_trans = define_iid_random_variable_transformation( uniform(),num_vars) c = np.random.uniform(0.,1.,num_vars) c*=20/c.sum() w = np.zeros_like(c); w[0] = np.random.uniform(0.,1.,1) model = GenzFunction( "oscillatory", num_vars,c=c,w=w) generate_parameter_sweeps_and_plot( model,{'ranges':var_trans.get_ranges()}, "parameter-sweeps-test-dir/genz-parameter-sweeps-test.npz", 'hypercube',num_sweeps=2,show=False) # png file save in test-dir do not remove dir if want to check png file shutil.rmtree('parameter-sweeps-test-dir/') plt.show()
def test_oli_leja_interpolation(self): num_vars=2 degree=5 poly = PolynomialChaosExpansion() var_trans = define_iid_random_variable_transformation( uniform(),num_vars) opts = define_poly_options_from_variable_transformation(var_trans) poly.configure(opts) indices = compute_hyperbolic_indices(num_vars,degree,1.0) poly.set_indices(indices) # candidates must be generated in canonical PCE space num_candidate_samples = 10000 generate_candidate_samples=lambda n: np.cos( np.random.uniform(0.,np.pi,(num_vars,n))) generate_candidate_samples=lambda n: (np.cos( np.random.uniform(0.,np.pi,(num_vars,n)))+1)/2. # must use canonical_basis_matrix to generate basis matrix num_leja_samples = indices.shape[1]-1 precond_func = lambda samples: 1./christoffel_function( samples,poly.basis_matrix) samples, data_structures = get_oli_leja_samples( poly,generate_candidate_samples, num_candidate_samples,num_leja_samples, preconditioning_function=precond_func) #samples = var_trans.map_from_canonical_space(samples) assert samples.max()<=1 and samples.min()>=0. c = np.random.uniform(0.,1.,num_vars) c*=20/c.sum() w = np.zeros_like(c); w[0] = np.random.uniform(0.,1.,1) genz_function = GenzFunction('oscillatory',num_vars,c=c,w=w) values = genz_function(samples) # Ensure we have produced an interpolant oli_solver = data_structures[0] poly = oli_solver.get_current_interpolant(samples,values) assert np.allclose(poly(samples),values)
def setup_genz_function(nvars, test_name, coefficients=None): r""" Setup the Genz Benchmarks. For example, the two-dimensional oscillatory Genz problem can be defined using >>> from pyapprox.benchmarks.benchmarks import setup_benchmark >>> benchmark=setup_benchmark('genz',nvars=2,test_name='oscillatory') >>> print(benchmark.keys()) dict_keys(['fun', 'mean', 'variable']) Parameters ---------- nvars : integer The number of variables of the Genz function test_name : string The test_name of the specific Genz function. See notes for options the string needed is given in brackets e.g. ('oscillatory') coefficients : tuple (ndarray (nvars), ndarray (nvars)) The coefficients :math:`c_i` and :math:`w_i` If None (default) then :math:`c_j = \hat{c}_j\left(\sum_{i=1}^d \hat{c}_i\right)^{-1}` where :math:`\hat{c}_i=(10^{-15\left(\frac{i}{d}\right)^2)})` Returns ------- benchmark : pya.Benchmark Object containing the benchmark attributes References ---------- .. [Genz1984] `Genz, A. Testing multidimensional integration routines. In Proc. of international conference on Tools, methods and languages for scientific and engineering computation (pp. 81-94), 1984 <https://dl.acm.org/doi/10.5555/2837.2842>`_ Notes ----- Corner Peak ('corner-peak') .. math:: f(z)=\left( 1+\sum_{i=1}^d c_iz_i\right)^{-(d+1)} Oscillatory ('oscillatory') .. math:: f(z) = \cos\left(2\pi w_1 + \sum_{i=1}^d c_iz_i\right) Gaussian Peak ('gaussian-peak') .. math:: f(z) = \exp\left( -\sum_{i=1}^d c_i^2(z_i-w_i)^2\right) Continuous ('continuous') .. math:: f(z) = \exp\left( -\sum_{i=1}^d c_i\lvert z_i-w_i\rvert\right) Product Peak ('product-peak') .. math:: f(z) = \prod_{i=1}^d \left(c_i^{-2}+(z_i-w_i)^2\right)^{-1} Discontinuous ('discontinuous') .. math:: f(z) = \begin{cases}0 & x_1>u_1 \;\mathrm{or}\; x_2>u_2\\\exp\left(\sum_{i=1}^d c_iz_i\right) & \mathrm{otherwise}\end{cases} """ genz = GenzFunction(test_name, nvars) univariate_variables = [stats.uniform(0, 1)] * nvars variable = pya.IndependentMultivariateRandomVariable(univariate_variables) if coefficients is None: genz.set_coefficients(1, 'squared-exponential-decay', 0) else: genz.c, genz.w = coefficients attributes = {'fun': genz, 'mean': genz.integrate(), 'variable': variable} if test_name == 'corner-peak': attributes['variance'] = genz.variance() from scipy.optimize import OptimizeResult return Benchmark(attributes)
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()
error /= np.linalg.norm(validation_values, axis=0) return error np.random.seed(1) #%% # Our goal is to demonstrate how to use a polynomial chaos expansion (PCE) to approximate a function :math:`f(z): \reals^d \rightarrow \reals` parameterized by the random variables :math:`z=(z_1,\ldots,z_d)`. with the joint probability density function :math:`\pdf(\V{\rv})`. In the following we will use a function commonly used in the literature, the oscillatory Genz function. This function is well suited for testing as the number of variables and the non-linearity can be adjusted. We define the random variables and the function with the following code univariate_variables = [uniform(), beta(3, 3)] variable = pya.IndependentMultivariateRandomVariable(univariate_variables) c = np.array([10, 0.01]) model = GenzFunction("oscillatory", variable.num_vars(), c=c, w=np.zeros_like(c)) #%% # Here we have intentionally set the coefficients :math:`c`: of the Genz function to be highly anisotropic, to emphasize the properties of the adaptive algorithm. # # PCE represent the model output :math:`f(\V{\rv})` as an expansion in orthonormal polynomials, # # .. math:: # # \begin{align*} # f(\V{\rv}) &\approx f_N(\V{\rv}) = \sum_{\lambda\in\Lambda}\alpha_{\lambda}\phi_{\lambda}(\V{\rv}), & |\Lambda| &= N. # \end{align*} # # where :math:`\lambda=(\lambda_1\ldots,\lambda_d)\in\mathbb{N}_0^d` is a multi-index and :math:`\Lambda` specifies the terms included in the expansion. In :ref:`Polynomial Chaos Regression` we set :math:`\Lambda` to be a total degree expansion. This choice was somewhat arbitray. The exact indices in :math:`\Lambda` should be chosen with more care. The number of terms in a PCE dictates how many samples are need to accurately compute the coefficients of the expansion. Consequently we should choose the index set :math:`\Lambda` in a way that minimizes error for a fixed computational budget. In this tutorial we use an adaptive algorithm to construct an index set that greedily minimizes the error in the PCE. Before starting the adaptive algorithm we must first define the PCE.
def test_lu_leja_interpolation_with_intial_samples(self): num_vars=2 degree=15 poly = PolynomialChaosExpansion() var_trans = define_iid_random_variable_transformation( uniform(),num_vars) opts = define_poly_options_from_variable_transformation(var_trans) poly.configure(opts) indices = compute_hyperbolic_indices(num_vars,degree,1.0) poly.set_indices(indices) # candidates must be generated in canonical PCE space num_candidate_samples = 10000 generate_candidate_samples=lambda n: np.cos( np.random.uniform(0.,np.pi,(num_vars,n))) # enforcing lu interpolation to interpolate a set of initial points # before selecting best samples from candidates can cause ill conditioning # to avoid this issue build a leja sequence and use this as initial # samples and then recompute sequence with different candidates # must use canonical_basis_matrix to generate basis matrix num_initial_samples = 5 initial_samples = None num_leja_samples = indices.shape[1]-1 precond_func = lambda matrix, samples: christoffel_weights(matrix) initial_samples, data_structures = get_lu_leja_samples( poly.canonical_basis_matrix,generate_candidate_samples, num_candidate_samples,num_initial_samples, preconditioning_function=precond_func, initial_samples=initial_samples) samples, data_structures = get_lu_leja_samples( poly.canonical_basis_matrix,generate_candidate_samples, num_candidate_samples,num_leja_samples, preconditioning_function=precond_func, initial_samples=initial_samples) assert np.allclose(samples[:,:num_initial_samples],initial_samples) samples = var_trans.map_from_canonical_space(samples) assert samples.max()<=1 and samples.min()>=0. c = np.random.uniform(0.,1.,num_vars) c*=20/c.sum() w = np.zeros_like(c); w[0] = np.random.uniform(0.,1.,1) genz_function = GenzFunction('oscillatory',num_vars,c=c,w=w) values = genz_function(samples) # Ensure coef produce an interpolant coef = interpolate_lu_leja_samples(samples,values,data_structures) # Ignore basis functions (columns) that were not considered during the # incomplete LU factorization poly.set_indices(poly.indices[:,:num_leja_samples]) poly.set_coefficients(coef) assert np.allclose(poly(samples),values) quad_w = get_quadrature_weights_from_lu_leja_samples( samples,data_structures) values_at_quad_x = values[:,0] assert np.allclose( np.dot(values_at_quad_x,quad_w),genz_function.integrate(), atol=1e-4)