def plot_discrete_distribution_surface_2d(rv1, rv2, ax=None): """ Only works if rv1 and rv2 are defined on consecutive integers """ from matplotlib import cm from pyapprox.utilities import cartesian_product, outer_product from pyapprox.variables import get_probability_masses if ax is None: fig = plt.figure(figsize=(8, 6)) ax = fig.add_subplot(111, projection='3d') x_1d = [get_probability_masses(rv)[0] for rv in [rv1, rv2]] w_1d = [get_probability_masses(rv)[1] for rv in [rv1, rv2]] samples = cartesian_product(x_1d) weights = outer_product(w_1d) dz = weights cmap = cm.get_cmap('jet') # Get desired colormap - you can change this! max_height = np.max(dz) # get range of colorbars so we can normalize min_height = np.min(dz) # scale each z to [0,1], and get their rgb values rgba = [cmap((k-min_height)/max_height) for k in dz] # Only works if rv1 and rv2 are defined on consecutive integers dx, dy = 1, 1 ax.bar3d(samples[0, :], samples[1, :], 0, dx, dy, dz, color=rgba, zsort='average') angle = 45 ax.view_init(10, angle) ax.set_axis_off()
def compute_multivariate_orthonormal_basis_product(product_coefs_1d,poly_index_ii,poly_index_jj,max_degrees1,max_degrees2,tol=2*np.finfo(float).eps): """ Compute the product of two multivariate orthonormal bases and re-express as an expansion using the orthnormal basis. """ num_vars = poly_index_ii.shape[0] poly_index= poly_index_ii+poly_index_jj active_vars = np.where(poly_index>0)[0] if active_vars.shape[0]>0: coefs_1d = [] for dd in active_vars: pii,pjj=poly_index_ii[dd],poly_index_jj[dd] if pii<pjj: tmp=pjj; pjj=pii; pii=tmp kk = flattened_rectangular_lower_triangular_matrix_index( pii,pjj,max_degrees1[dd]+1,max_degrees2[dd]+1) coefs_1d.append(product_coefs_1d[dd][kk][:,0]) indices_1d = [np.arange(poly_index[dd]+1) for dd in active_vars] product_coefs = outer_product(coefs_1d)[:,np.newaxis] active_product_indices = cartesian_product(indices_1d) II = np.where(np.absolute(product_coefs)>tol)[0] active_product_indices = active_product_indices[:,II] product_coefs = product_coefs[II] product_indices = np.zeros( (num_vars,active_product_indices.shape[1]),dtype=int) product_indices[active_vars]=active_product_indices else: product_coefs = np.ones((1,1)) product_indices = np.zeros([num_vars,1],dtype=int) return product_indices, product_coefs
def compute_tensor_product_level_indices(num_vars, degree, max_norm=True): indices = cartesian_product([np.arange(degree+1)]*num_vars, 1) if max_norm: II = np.where(np.linalg.norm(indices, axis=0, ord=np.inf) == degree)[0] else: II = np.where(indices.sum(axis=0) == degree)[0] return indices[:, II]
def product_of_independent_random_variables_pdf(pdf,gauss_quadrature_rules,zz): """ Compute PDF of Z = X_1*X_2*...*X_d Parameters --------- pdf : callable PDF of X_1 gauss_quadrature_rules : list [x,w] List of gaussian quadrature rules which integrate with respect to PDFs of X_2,...X_d zz : np.ndarray (num_samples) The locations to evaluate the pdf of Z """ num_vars = len(gauss_quadrature_rules)+1 xx = cartesian_product( [gauss_quadrature_rules[ii][0] for ii in range(num_vars-1)]) ww = outer_product( [gauss_quadrature_rules[ii][1] for ii in range(num_vars-1)]) vals = np.zeros_like(zz) for ii in range(vals.shape[0]): vals[ii] = np.dot( ww,pdf(zz[ii]/xx.prod(axis=0))/np.absolute(xx.prod(axis=0))) return vals
def predictor_corrector_function_of_independent_variables( nterms, univariate_quad_rules, fun): """ Use predictor corrector method to compute the recursion coefficients of a univariate orthonormal polynomial orthogonal to the density associated with a scalar function of a set of independent 1D variables Parameters ---------- nterms : integer The number of coefficients requested univariate_quad_rules : callable The univariate quadrature rules which include weights of each indendent variable fun : callable The function mapping indendent variables into a scalar variable """ ab = np.zeros((nterms, 2)) x_1d = [rule[0] for rule in univariate_quad_rules] w_1d = [rule[1] for rule in univariate_quad_rules] quad_samples = cartesian_product(x_1d, 1) quad_weights = outer_product(w_1d) # for probablity measures the following will always be one, but # this is not true for other measures ab[0, 1] = np.sqrt(quad_weights.sum()) for ii in range(1, nterms): # predict ab[ii, 1] = ab[ii - 1, 1] if ii > 1: ab[ii - 1, 0] = ab[ii - 2, 0] else: ab[ii - 1, 0] = 0 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] * pvals[:, ii - 1] G_ii_iim1 = integrand(quad_samples).dot(quad_weights) ab[ii - 1, 0] += ab[ii - 1, 1] * G_ii_iim1 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 G_ii_ii = integrand(quad_samples).dot(quad_weights) ab[ii, 1] *= np.sqrt(G_ii_ii) return ab
def test_get_chebyhsev_center_of_inactive_subspace(self): # Define an active subspace num_vars = 6 num_active_vars = 2 W, W1, W2 = get_random_active_subspace_eigenvecs( num_vars, num_active_vars) num_active_samples = 100 # generate samples in the active subspace zonotope samples = np.random.uniform(-1., 1., (num_vars, num_active_samples)) active_samples = np.dot(W1.T, samples) # get active coordinates of hypercube vertices. # Warning. Because of approximate nature of find_zonotope_vertices # algorithm not all mapped vertices may lie within the # linear constraints that the find vertices algorithm generates hypercube_vertices_1d = np.array([-1., 1.]) hypercube_vertices = cartesian_product( [hypercube_vertices_1d]*num_vars, 1) mapped_vertices = np.dot(W1.T, hypercube_vertices) active_samples = np.hstack((active_samples, mapped_vertices)) samples = transform_active_subspace_samples_to_original_coordinates( active_samples, W) # check the sample in the original space fits inside the # hypercube. Allow for slight machine precision error assert np.all( np.absolute(samples) <= 1+4*np.finfo(float).eps)
def test_leja_objective_2d(self): num_vars = 2 alpha_stat, beta_stat = [2, 2] #alpha_stat,beta_stat = [1,1] weight_function, weight_function_deriv, poly = self.setup( num_vars, alpha_stat, beta_stat) leja_sequence = np.array([[-1.0, -1.0], [1.0, 1.0]]).T degree = 1 indices = compute_hyperbolic_indices(num_vars, degree, 1.0) # sort lexographically to make testing easier I = np.lexsort((indices[0, :], indices[1, :], indices.sum(axis=0))) indices = indices[:, I] poly.set_indices(indices[:, :2]) new_indices = indices[:, 2:3] coeffs = compute_coefficients_of_leja_interpolant( leja_sequence, poly, new_indices, weight_function) sample = np.asarray([0.5, -0.5])[:, np.newaxis] func = partial(leja_objective, leja_sequence=leja_sequence, poly=poly, new_indices=new_indices, coeff=coeffs, weight_function=weight_function, weight_function_deriv=weight_function_deriv) fd_eps = 1e-7 fd_deriv = compute_finite_difference_derivative(func, sample, fd_eps=fd_eps) residual, jacobian = leja_objective_and_gradient(sample, leja_sequence, poly, new_indices, coeffs, weight_function, weight_function_deriv, deriv_order=1) grad = np.dot(jacobian.T, residual) assert np.allclose(fd_deriv, grad, atol=fd_eps * 100) num_samples = 20 samples = np.linspace(-1, 1, num_samples) samples = cartesian_product([samples] * num_vars) objective_vals = func(samples) f, ax = plt.subplots(1, 1, figsize=(8, 6)) X = samples[0, :].reshape(num_samples, num_samples) Y = samples[1, :].reshape(num_samples, num_samples) Z = objective_vals.reshape(num_samples, num_samples) cset = ax.contourf(X, Y, Z, levels=np.linspace(Z.min(), Z.max(), 30), cmap=None) plt.colorbar(cset) plt.plot(leja_sequence[0, :], leja_sequence[1, :], 'ko', ms=20)
def test_inner_products_on_active_subspace(self): num_vars=4; num_active_vars = 2; degree=3; A = np.random.normal(0,1,(num_vars,num_vars)) Q, R = np.linalg.qr( A ) W1 = Q[:,:num_active_vars] as_poly_indices = np.asarray([ [0,0],[1,0],[0,1],[2,0],[1,1],[0,2] ]).T x1d,w1d = np.polynomial.legendre.leggauss(10) w1d /=2 gl_samples = cartesian_product([x1d]*num_vars) gl_weights = outer_product([w1d]*num_vars) as_gl_samples = np.dot(W1.T,gl_samples) inner_product_indices = np.empty( (num_active_vars,as_poly_indices.shape[1]**2),dtype=int) for ii in range(as_poly_indices.shape[1]): for jj in range(as_poly_indices.shape[1]): inner_product_indices[:,ii*as_poly_indices.shape[1]+jj]=\ as_poly_indices[:,ii]+as_poly_indices[:,jj] vandermonde = monomial_basis_matrix(inner_product_indices,as_gl_samples) inner_products = inner_products_on_active_subspace( W1.T,as_poly_indices,monomial_mean_uniform_variables) for ii in range(as_poly_indices.shape[1]): for jj in range(as_poly_indices.shape[1]): assert np.allclose( inner_products[ii,jj], np.dot(vandermonde[:,ii*as_poly_indices.shape[1]+jj], gl_weights))
def test_tensor_product_lagrange_interpolation(self): nvars = 5 level = 10 x = gauss_hermite_pts_wts_1D(level + 1)[0] #active_vars = np.arange(nvars) active_vars = np.hstack([np.arange(2), np.arange(3, nvars)]) nactive_vars = active_vars.shape[0] abscissa_1d = [x] * nactive_vars power = x.shape[0] - 2 def fun(samples): return np.sum(samples[active_vars, :]**power, axis=0)[:, None] nsamples = 1000 validation_samples = np.random.normal(0, 1, (nvars, nsamples)) zz = abscissa_1d.copy() zz.insert(2, np.zeros(1)) train_samples = cartesian_product(zz) values = fun(train_samples) approx_values = tensor_product_lagrange_interpolation( validation_samples, abscissa_1d, active_vars, values) barycentric_weights_1d = [ compute_barycentric_weights_1d(x) for x in abscissa_1d ] poly_vals = multivariate_barycentric_lagrange_interpolation( validation_samples, abscissa_1d, barycentric_weights_1d, values, active_vars) assert np.allclose(approx_values, fun(validation_samples)) assert np.allclose(poly_vals, fun(validation_samples))
def tensor_product_indices(degrees): num_vars = len(degrees) indices_1d = [] for ii in range(num_vars): indices_1d.append(np.arange(degrees[ii]+1)) indices = cartesian_product(indices_1d, 1) return indices
def tensor_product_lagrange_interpolation(samples, abscissa_1d, active_vars, values): assert len(abscissa_1d) == len(active_vars) active_indices = cartesian_product( [np.arange(x.shape[0]) for x in abscissa_1d]) basis_vals_1d = precompute_tensor_product_lagrange_polynomial_basis( samples, abscissa_1d, active_vars) return __tensor_product_lagrange_polynomial_basis(samples, basis_vals_1d, active_vars, values, active_indices)
def get_tensor_product_points(level,var_trans,quad_type): abscissa_1d = [] num_vars = var_trans.num_vars() if quad_type == 'CC': x, w = clenshaw_curtis_pts_wts_1D(level) elif quad_type == 'GH': x, w = gauss_hermite_pts_wts_1D(level) for dd in range( num_vars ): abscissa_1d.append( x ) pts = cartesian_product( abscissa_1d, 1 ) pts = var_trans.map_from_canonical_space( pts ) return pts
def plot_tensor_product_lagrange_basis_2d(level, ii, jj, ax=None): abscissa, tmp = clenshaw_curtis_pts_wts_1D(level) abscissa_1d = [abscissa, abscissa] barycentric_weights_1d = [compute_barycentric_weights_1d(abscissa_1d[0]), compute_barycentric_weights_1d(abscissa_1d[1])] training_samples = cartesian_product(abscissa_1d, 1) fn_vals = np.zeros((training_samples.shape[1], 1)) idx = jj*abscissa_1d[1].shape[0]+ii fn_vals[idx] = 1. def f(samples): return multivariate_barycentric_lagrange_interpolation( samples, abscissa_1d, barycentric_weights_1d, fn_vals, np.array([0, 1])) plot_limits = [-1, 1, -1, 1] num_pts_1d = 101 X, Y, Z = get_meshgrid_function_data(f, plot_limits, num_pts_1d) if ax is None: ax = create_3d_axis() cmap = mpl.cm.coolwarm plot_surface(X, Y, Z, ax, axis_labels=None, limit_state=None, alpha=0.3, cmap=mpl.cm.coolwarm, zorder=3, plot_axes=False) num_contour_levels = 30 offset = -(Z.max()-Z.min())/2 cmap = mpl.cm.gray ax.contourf( X, Y, Z, zdir='z', offset=offset, levels=np.linspace(Z.min(), Z.max(), num_contour_levels), cmap=cmap, zorder=-1) ax.plot(training_samples[0, :], training_samples[1, :], offset*np.ones(training_samples.shape[1]), 'o', zorder=100, color='b') x = np.linspace(-1, 1, 100) y = training_samples[1, idx]*np.ones((x.shape[0])) z = f(np.vstack((x[np.newaxis, :], y[np.newaxis, :])))[:, 0] ax.plot(x, Y.max()*np.ones((x.shape[0])), z, '-r') ax.plot(abscissa_1d[0], Y.max()*np.ones( (abscissa_1d[0].shape[0])), np.zeros(abscissa_1d[0].shape[0]), 'or') y = np.linspace(-1, 1, 100) x = training_samples[0, idx]*np.ones((y.shape[0])) z = f(np.vstack((x[np.newaxis, :], y[np.newaxis, :])))[:, 0] ax.plot(X.min()*np.ones((x.shape[0])), y, z, '-r') ax.plot(X.min()*np.ones( (abscissa_1d[1].shape[0])), abscissa_1d[1], np.zeros(abscissa_1d[1].shape[0]), 'or')
def ortho_polynomial_grammian_bounded_continuous_variable( var, ab, degree, tol, integrate_fun=None): """ Compute the inner product of all polynomials up to and including degree. Useful for testing that the polynomials are orthonormal. The grammian should always be the identity (modulo errors due to quadrature) """ if ab.shape[0] < degree + 1: raise ValueError("Not enough recursion coefficients") loc, scale = transform_scale_parameters(var) if is_bounded_continuous_variable(var): can_lb, can_ub = -1, 1 else: lb, ub = var.interval(1) can_lb = (lb - loc) / scale can_ub = (ub - loc) / scale def default_integrate(integrand): result = scipy.integrate.quad(integrand, can_lb, can_ub, epsabs=tol, epsrel=tol) return result[0] if integrate_fun is None: integrate = default_integrate else: integrate = partial(integrate_fun, can_lb, can_ub) def fun(order1, order2): order = max(order1, order2) def integrand(x): x = np.atleast_1d(x) basis_mat = evaluate_orthonormal_polynomial_1d(x, order, ab) return var.pdf(x * scale + loc) * scale * (basis_mat[:, order1] * basis_mat[:, order2]) return integrate(integrand) vec_fun = np.vectorize(fun) indices = cartesian_product((np.arange(degree + 1), np.arange(degree + 1))) gram_mat = vec_fun(indices[0, :], indices[1, :]) return gram_mat.reshape((degree + 1, degree + 1))
def test_inner_products_on_active_subspace_using_samples(self): def generate_samples(num_samples): from pyapprox.low_discrepancy_sequences import \ transformed_halton_sequence samples = transformed_halton_sequence(None, num_vars, num_samples) samples = samples*2.-1. return samples num_vars = 4 num_active_vars = 2 degree = 3 A = np.random.normal(0, 1, (num_vars, num_vars)) Q, R = np.linalg.qr(A) W1 = Q[:, :num_active_vars] as_poly_indices = np.asarray([ [0, 0], [1, 0], [0, 1], [2, 0], [1, 1], [0, 2] ]).T x1d, w1d = np.polynomial.legendre.leggauss(10) w1d /= 2 gl_samples = cartesian_product([x1d]*num_vars) gl_weights = outer_product([w1d]*num_vars) as_gl_samples = np.dot(W1.T, gl_samples) inner_product_indices = np.empty( (num_active_vars, as_poly_indices.shape[1]**2), dtype=int) for ii in range(as_poly_indices.shape[1]): for jj in range(as_poly_indices.shape[1]): inner_product_indices[:, ii*as_poly_indices.shape[1]+jj] =\ as_poly_indices[:, ii]+as_poly_indices[:, jj] vandermonde = monomial_basis_matrix( inner_product_indices, as_gl_samples) num_sobol_samples = 100000 inner_products = sample_based_inner_products_on_active_subspace( W1, monomial_basis_matrix, as_poly_indices, num_sobol_samples, generate_samples) for ii in range(as_poly_indices.shape[1]): for jj in range(as_poly_indices.shape[1]): assert np.allclose( inner_products[ii, jj], np.dot(vandermonde[:, ii*as_poly_indices.shape[1]+jj], gl_weights), atol=1e-4)
def integrate(self, mesh_values, order=None): if order is None: order = self.order # Get Gauss-Legendre rule gl_pts, gl_wts = gauss_jacobi_pts_wts_1D(order, 0, 0) pts_1d, wts_1d = [], [] lims = self.xlim+self.ylim for ii in range(2): # Scale points from [-1,1] to to physical domain x_range = lims[2*ii+1]-lims[2*ii] # Remove factor of 0.5 from weights and shift to [a,b] wts_1d.append(gl_wts*x_range) pts_1d.append(x_range*(gl_pts+1.)/2.+lims[2*ii]) # Interpolate mesh values onto quadrature nodes pts = cartesian_product(pts_1d) wts = outer_product(wts_1d) gl_vals = self.interpolate(mesh_values, pts) # Compute and return integral return np.dot(gl_vals[:, 0], wts)
def evaluate_active_subspace_density_1d_example(density_fn, tol, test=False): from pyapprox.visualization import plot_2d_polygon, \ get_meshgrid_function_data num_quad_samples = 100 points_for_eval, quad_weights = np.polynomial.legendre.leggauss( num_quad_samples) # for varing rotations make sure density on 1d active subspace # integrates to 1 n = 10 r = 1 num_rotations = 23 angles = np.linspace(0., np.pi * 2., num_rotations) import matplotlib.pyplot as plt f, axs = plt.subplots(1, 2, sharey=False, figsize=(16, 6)) for i in range(1, angles.shape[0]): W = np.array([[np.cos(angles[i]), -np.sin(angles[i])], [np.sin(angles[i]), np.cos(angles[i])]]) density_vals, subspace_1d_bounds = \ evaluate_active_subspace_density_1d( W,density_fn,points_for_eval,num_quad_samples,False) integral_density = np.dot(density_vals, quad_weights) # Gauss legendre weights must be scaled to correct for length of # subspace interval. If integrating # int_a^b f(t)dt but gauss points are x in [-1,1] then dt=(b-a)/2 dx integral_density *= (subspace_1d_bounds[1] - subspace_1d_bounds[0]) / 2 #print abs(1-integral_density) assert np.allclose(integral_density, 1., atol=tol) if not test: axs[0].plot(map_m11_to_ab(points_for_eval, subspace_1d_bounds[0], subspace_1d_bounds[1]), density_vals, 'k', lw=2) hypercube_vertices_1d = np.array([-1., 1.]) hypercube_vertices = cartesian_product([hypercube_vertices_1d] * 2, 1) mapped_vertices = np.dot(W, hypercube_vertices) if not test: plot_2d_polygon(mapped_vertices, axs[1]) if not test: plt.show()
def test_moments_of_active_subspace_II(self): num_vars=4; num_active_vars = 2; degree=12; A = np.random.normal(0,1,(num_vars,num_vars)) Q, R = np.linalg.qr( A ) W1 = Q[:,:num_active_vars] as_poly_indices = compute_hyperbolic_indices(num_active_vars,degree,1.0) moments = moments_of_active_subspace( W1.T, as_poly_indices, monomial_mean_uniform_variables) x1d,w1d = np.polynomial.legendre.leggauss(10) w1d /=2 gl_samples = cartesian_product([x1d]*num_vars) gl_weights = outer_product([w1d]*num_vars) as_gl_samples = np.dot(W1.T,gl_samples) vandermonde = monomial_basis_matrix(as_poly_indices,as_gl_samples) quad_poly_moments = np.empty(vandermonde.shape[1]) for ii in range(vandermonde.shape[1]): quad_poly_moments[ii] = np.dot(vandermonde[:,ii],gl_weights) assert np.allclose(moments,quad_poly_moments)
def get_subspace_polynomial_indices(subspace_index, growth_rule_1d, config_variables_idx=None): """ Get the polynomial indices of a tensor-product nodal subspace. Parameters ---------- subspace index : np.ndarray (num_vars) The subspace index [l_1,...,l_d] growth_rule_1d : list of callable functions Function which takes a level l_i as its only argument and returns the number of samples in the 1D quadrature rule of the specified level. Return ------ poly_indices : np.ndarray (num_vars x num_subspace_samples) The polynomial indices of the tensor-product subspace. """ subspace_index = np.asarray(subspace_index) num_vars = subspace_index.shape[0] if np.all(subspace_index == 0): return np.zeros((num_vars, 1), dtype=int) if config_variables_idx is None: config_variables_idx = num_vars assert len(growth_rule_1d) == config_variables_idx poly_indices_1d = [] for ii in range(num_vars): if ii < config_variables_idx: poly_indices_1d.append( np.arange(growth_rule_1d[ii](subspace_index[ii]))) else: # for config variables just set value equal to subspace index value poly_indices_1d.append(np.asarray([subspace_index[ii]])) poly_indices = cartesian_product(poly_indices_1d, 1) return poly_indices
def initialize(self, order, bndry_cond=None, lims=None): # 1d model transforms mesh pts 1d from are on [-1,1] to [a,b] # I will asssume that second physical dimension is also [a,b] super(SteadyStateDiffusionEquation2D, self).initialize(order, bndry_cond[:2], lims[:2]) self.ylim = lims[2:] self.bndry_cond = bndry_cond self.order = order self.mesh_pts_1d = self.mesh_pts self.mesh_pts = cartesian_product([self.mesh_pts_1d]*2, 1) # note scaling of self.derivative_matrix to [a,b] happens at base class self.determine_boundary_indices() # form derivative (in x1-direction) matrix of a 2d polynomial # this assumes that 2d-mesh_pts varies in x1 faster than x2, # e.g. points are # [[x11,x21],[x12,x21],[x13,x12],[x11,x22],[x12,x22],...] Ident = np.eye(self.order+1) derivative_matrix_1d = self.get_derivative_matrix() self.derivative_matrix_1 = np.kron(Ident, derivative_matrix_1d) # form derivative (in x2-direction) matrix of a 2d polynomial self.derivative_matrix_2 = np.kron(derivative_matrix_1d, Ident)
def error_vs_cost(model, generate_random_samples, validation_levels): #import sys #sys.setrecursionlimit(10) #model=WorkTrackingModel(model,model.base_model) num_samples = 10 validation_levels = np.asarray(validation_levels) assert len(validation_levels) == model.base_model.num_config_vars config_vars = cartesian_product( [np.arange(ll) for ll in validation_levels]) random_samples = generate_random_samples(num_samples) samples = get_all_sample_combinations(random_samples, config_vars) reference_samples = samples[:, ::config_vars.shape[1]].copy() reference_samples[-model.base_model.num_config_vars:,:]=\ validation_levels[:,np.newaxis] reference_values = model(reference_samples) reference_mean = reference_values[:, 0].mean() values = model(samples) # put keys in order returned by cartesian product keys = sorted(model.work_tracker.costs.keys(), key=lambda x: x[::-1]) keys = keys[: -1] # remove validation key associated with validation samples costs, ndofs, means, errors = [], [], [], [] for ii in range(len(keys)): key = keys[ii] costs.append(np.median(model.work_tracker.costs[key])) nx, ny, dt = model.base_model.get_degrees_of_freedom_and_timestep( np.asarray(key)) ndofs.append(nx * ny * model.base_model.final_time / dt) print(key, ndofs[-1], nx, ny, model.base_model.final_time / dt) means.append(np.mean(values[ii::config_vars.shape[1], 0])) errors.append(abs(means[-1] - reference_mean) / abs(reference_mean)) times = costs.copy() # make costs relative costs /= costs[-1] n1, n2, n3 = validation_levels indices = np.reshape(np.arange(len(keys), dtype=int), (n1, n2, n3), order='F') costs = np.reshape(np.array(costs), (n1, n2, n3), order='F') ndofs = np.reshape(np.array(ndofs), (n1, n2, n3), order='F') errors = np.reshape(np.array(errors), (n1, n2, n3), order='F') times = np.reshape(np.array(times), (n1, n2, n3), order='F') validation_index = reference_samples[-model.base_model.num_config_vars:, 0] validation_time = np.median( model.work_tracker.costs[tuple(validation_levels)]) validation_cost = validation_time / costs[-1] validation_ndof = np.prod(reference_values[:, -2:], axis=1) data = { "costs": costs, "errors": errors, "indices": indices, "times": times, "validation_index": validation_index, "validation_cost": validation_cost, "validation_ndof": validation_ndof, "validation_time": validation_time, "ndofs": ndofs } return data
def evaluate_active_subspace_density_1d(W, density_fn, points_for_eval=None, num_quad_points=100, plot_steps=True): """ Assume we have hypercube on [-1,1]^d, d=2 W: matrix (d x d) rotation matrix usually the transpose of activesubspace eigenvectors num_quad_points: int number of quadrature points used to integrate 2d density along a line points_for_eval: vector (d) points at which to evalute the 1d active subspace density """ assert W.shape[0] == W.shape[1] == 2 num_dims = 2 #Split rotation matrix into active subspace eigenvector W1 = W[0, :] # and inactive subspace eigenvector W2 = W[1, :] # define hypercube vertices hypercube_vertices_1d = np.array([-1., 1.]) hypercube_vertices = cartesian_product([hypercube_vertices_1d] * num_dims, 1) # compute location of each hypercube vertices in the 1d active subspace. # multiple vertices may map to the same point mapped_vertices = np.dot(W1, hypercube_vertices).squeeze() mapped_vertices = np.sort(mapped_vertices) subspace_1d_bounds = mapped_vertices.min(), mapped_vertices.max() # compute location of each hypercube vertices in the rotated # (but still full dimensional) active subspace coordinates. rotated_vertices = np.dot(W, hypercube_vertices) # sort vertices for plotting rotated_vertices = sort_2d_vertices_by_polar_angle(rotated_vertices) # define points for plotting in 1d active subspace if points_for_eval is None: num_points_for_eval = 102 delta = (subspace_1d_bounds[1] - subspace_1d_bounds[0]) / (num_points_for_eval) points_for_eval = np.linspace(subspace_1d_bounds[0] + delta / 2., subspace_1d_bounds[1] - delta / 2., num_points_for_eval) else: # map subspace eval points from [-1,1] to [lb,ub] assert points_for_eval.min() >= -1 and points_for_eval.max() <= 1 points_for_eval = map_m11_to_ab(points_for_eval, subspace_1d_bounds[0], subspace_1d_bounds[1]) # Initialize memory and counters cnt_all = 0 density_vals = np.zeros(points_for_eval.shape[0], float) for i in range(mapped_vertices.shape[0] - 1): # Find points in 1d active subspace that map to the # current edge of the two dimensional hypercube I = np.where((points_for_eval >= mapped_vertices[i]) & (points_for_eval < mapped_vertices[i + 1]))[0] points_for_eval_in_interval = points_for_eval[I] num_points_for_eval_in_interval = points_for_eval_in_interval.shape[0] # Check that no points for eval coincide with the location of # the mappeed hypercube vertices. Such points will mess up search # for perpendicular line assert np.all(points_for_eval_in_interval != mapped_vertices[i]) assert np.all(points_for_eval_in_interval != mapped_vertices[i + 1]) # integrate desity on zonotope along the line perpendicular to the # active subspace that runs through the point # points_for_eval_in_interval[i] and extends to edges on original # hypercube. At some locations line may only touch # the boundary of the hyercube on one side of the active subspace for j in range(num_points_for_eval_in_interval): line = find_line_perpendicular_to_active_subspace( W, points_for_eval_in_interval[j]) density = integrate_density_a_2d_function_long_a_line( W, line, points_for_eval_in_interval[j], density_fn, num_quad_points=num_quad_points) points_for_eval[cnt_all] = points_for_eval_in_interval[j] density_vals[cnt_all] = density cnt_all += 1 if plot_steps: plot_evaluate_active_subspace_density_1d_step( line, points_for_eval_in_interval[j], rotated_vertices, density_fn, points_for_eval, cnt_all, W, mapped_vertices, density_vals) plt.show() assert cnt_all == points_for_eval.shape[0] return density_vals, subspace_1d_bounds
def test_discrete_induced_sampling(self): degree = 3 nmasses1 = 10 mass_locations1 = np.geomspace(1.0, 512.0, num=nmasses1) #mass_locations1 = np.arange(0,nmasses1) masses1 = np.ones(nmasses1, dtype=float) / nmasses1 var1 = float_rv_discrete(name='float_rv_discrete', values=(mass_locations1, masses1))() nmasses2 = 10 mass_locations2 = np.arange(0, nmasses2) # if increase from 16 unmodififed becomes ill conditioned masses2 = np.geomspace(1.0, 16.0, num=nmasses2) #masses2 = np.ones(nmasses2,dtype=float)/nmasses2 masses2 /= masses2.sum() var2 = float_rv_discrete(name='float_rv_discrete', values=(mass_locations2, masses2))() var_trans = AffineRandomVariableTransformation([var1, var2]) pce_opts = define_poly_options_from_variable_transformation(var_trans) pce = PolynomialChaosExpansion() pce.configure(pce_opts) indices = compute_hyperbolic_indices(pce.num_vars(), degree, 1.0) pce.set_indices(indices) num_samples = int(1e4) np.random.seed(1) canonical_samples = generate_induced_samples(pce, num_samples) samples = var_trans.map_from_canonical_space(canonical_samples) np.random.seed(1) canonical_xk = [ 2 * get_distribution_info(var1)[2]['xk'] - 1, 2 * get_distribution_info(var2)[2]['xk'] - 1 ] basis_matrix_generator = partial(basis_matrix_generator_1d, pce, degree) canonical_samples1 = discrete_induced_sampling( basis_matrix_generator, pce.indices, canonical_xk, [var1.dist.pk, var2.dist.pk], num_samples) samples1 = var_trans.map_from_canonical_space(canonical_samples1) def density(x): return var1.pdf(x[0, :]) * var2.pdf(x[1, :]) envelope_factor = 30 def generate_proposal_samples(n): samples = np.vstack([var1.rvs(n), var2.rvs(n)]) return samples proposal_density = density # unlike fekete and leja sampling can and should use # pce.basis_matrix here. If use canonical_basis_matrix then # densities must be mapped to this space also which can be difficult samples2 = random_induced_measure_sampling(num_samples, pce.num_vars(), pce.basis_matrix, density, proposal_density, generate_proposal_samples, envelope_factor) def induced_density(x): vals = density(x) * christoffel_function(x, pce.basis_matrix, True) return vals from pyapprox.utilities import cartesian_product, outer_product from pyapprox.polynomial_sampling import christoffel_function quad_samples = cartesian_product([var1.dist.xk, var2.dist.xk]) quad_weights = outer_product([var1.dist.pk, var2.dist.pk]) #print(canonical_samples.min(axis=1),canonical_samples.max(axis=1)) #print(samples.min(axis=1),samples.max(axis=1)) #print(canonical_samples1.min(axis=1),canonical_samples1.max(axis=1)) #print(samples1.min(axis=1),samples1.max(axis=1)) # import matplotlib.pyplot as plt # plt.plot(quad_samples[0,:],quad_samples[1,:],'s') # plt.plot(samples[0,:],samples[1,:],'o') # plt.plot(samples1[0,:],samples1[1,:],'*') # plt.show() rtol = 1e-2 assert np.allclose(quad_weights, density(quad_samples)) assert np.allclose(density(quad_samples).sum(), 1) assert np.allclose( christoffel_function(quad_samples, pce.basis_matrix, True).dot(quad_weights), 1.0) true_induced_mean = quad_samples.dot(induced_density(quad_samples)) print(true_induced_mean) print(samples.mean(axis=1)) print(samples1.mean(axis=1)) print(samples2.mean(axis=1)) print( samples1.mean(axis=1) - true_induced_mean, true_induced_mean * rtol) #print(samples2.mean(axis=1)) assert np.allclose(samples.mean(axis=1), true_induced_mean, rtol=rtol) assert np.allclose(samples1.mean(axis=1), true_induced_mean, rtol=rtol) assert np.allclose(samples2.mean(axis=1), true_induced_mean, rtol=rtol)
def help_compare_prediction_based_oed(self, deviation_fun, gauss_deviation_fun, use_gauss_quadrature, ninner_loop_samples, ndesign_vars, tol): ncandidates_1d = 5 design_candidates = cartesian_product( [np.linspace(-1, 1, ncandidates_1d)] * ndesign_vars) ncandidates = design_candidates.shape[1] # Define model used to predict likely observable data indices = compute_hyperbolic_indices(ndesign_vars, 1)[:, 1:] Amat = monomial_basis_matrix(indices, design_candidates) obs_fun = partial(linear_obs_fun, Amat) # Define model used to predict unobservable QoI qoi_fun = exponential_qoi_fun # Define the prior PDF of the unknown variables nrandom_vars = indices.shape[1] prior_variable = IndependentMultivariateRandomVariable( [stats.norm(0, 0.5)] * nrandom_vars) # Define the independent observational noise noise_std = 1 # Define initial design init_design_indices = np.array([ncandidates // 2]) # Define OED options nouter_loop_samples = 100 if use_gauss_quadrature: # 301 needed for cvar deviation # only 31 needed for variance deviation ninner_loop_samples_1d = ninner_loop_samples var_trans = AffineRandomVariableTransformation(prior_variable) x_quad, w_quad = gauss_hermite_pts_wts_1D(ninner_loop_samples_1d) x_quad = cartesian_product([x_quad] * nrandom_vars) w_quad = outer_product([w_quad] * nrandom_vars) x_quad = var_trans.map_from_canonical_space(x_quad) ninner_loop_samples = x_quad.shape[1] def generate_inner_prior_samples(nsamples): assert nsamples == x_quad.shape[1], (nsamples, x_quad.shape) return x_quad, w_quad else: # use default Monte Carlo sampling generate_inner_prior_samples = None # Define initial design init_design_indices = np.array([ncandidates // 2]) # Setup OED problem oed = BayesianBatchDeviationOED(design_candidates, obs_fun, noise_std, prior_variable, qoi_fun, nouter_loop_samples, ninner_loop_samples, generate_inner_prior_samples, deviation_fun=deviation_fun) oed.populate() oed.set_collected_design_indices(init_design_indices) prior_mean = oed.prior_variable.get_statistics('mean') prior_cov = np.diag(prior_variable.get_statistics('var')[:, 0]) prior_cov_inv = np.linalg.inv(prior_cov) selected_indices = init_design_indices # Generate experimental design nexperiments = 3 for step in range(len(init_design_indices), nexperiments): # Copy current state of OED before new data is determined # This copy will be used to compute Laplace based utility and # evidence values for testing oed_copy = copy.deepcopy(oed) # Update the design utility_vals, selected_indices = oed.update_design() utility, deviations, evidences, weights = \ oed_copy.compute_expected_utility( oed_copy.collected_design_indices, selected_indices, True) exact_deviations = np.empty(nouter_loop_samples) for jj in range(nouter_loop_samples): # only test intermediate quantities associated with design # chosen by the OED step idx = oed.collected_design_indices obs_jj = oed_copy.outer_loop_obs[jj:jj + 1, idx] noise_cov_inv_jj = np.eye(idx.shape[0]) / noise_std**2 exact_post_mean_jj, exact_post_cov_jj = \ laplace_posterior_approximation_for_linear_models( Amat[idx, :], prior_mean, prior_cov_inv, noise_cov_inv_jj, obs_jj.T) exact_deviations[jj] = gauss_deviation_fun( exact_post_mean_jj, exact_post_cov_jj) print('d', np.absolute(exact_deviations - deviations[:, 0]).max(), tol) # print(exact_deviations, deviations[:, 0]) assert np.allclose(exact_deviations, deviations[:, 0], atol=tol) assert np.allclose(utility_vals[selected_indices], -np.mean(exact_deviations), atol=tol)
def test_evaluate_multivariate_orthonormal_polynomial(self): num_vars = 2 alpha = 0. beta = 0. degree = 2 deriv_order = 1 probability_measure = True ab = jacobi_recurrence(degree + 1, alpha=alpha, beta=beta, probability=probability_measure) x, w = np.polynomial.legendre.leggauss(degree) samples = cartesian_product([x] * num_vars, 1) indices = compute_hyperbolic_indices(num_vars, degree, 1.0) # sort lexographically to make testing easier II = np.lexsort((indices[0, :], indices[1, :], indices.sum(axis=0))) indices = indices[:, II] basis_matrix = evaluate_multivariate_orthonormal_polynomial( samples, indices, ab, deriv_order) exact_basis_vals_1d = [] exact_basis_derivs_1d = [] for dd in range(num_vars): x = samples[dd, :] exact_basis_vals_1d.append( np.asarray([1 + 0. * x, x, 0.5 * (3. * x**2 - 1)]).T) exact_basis_derivs_1d.append( np.asarray([0. * x, 1.0 + 0. * x, 3. * x]).T) exact_basis_vals_1d[-1] /= np.sqrt(1. / (2 * np.arange(degree + 1) + 1)) exact_basis_derivs_1d[-1] /= np.sqrt( 1. / (2 * np.arange(degree + 1) + 1)) exact_basis_matrix = np.asarray([ exact_basis_vals_1d[0][:, 0], exact_basis_vals_1d[0][:, 1], exact_basis_vals_1d[1][:, 1], exact_basis_vals_1d[0][:, 2], exact_basis_vals_1d[0][:, 1] * exact_basis_vals_1d[1][:, 1], exact_basis_vals_1d[1][:, 2] ]).T # x1 derivative exact_basis_matrix = np.vstack( (exact_basis_matrix, np.asarray([ 0. * x, exact_basis_derivs_1d[0][:, 1], 0. * x, exact_basis_derivs_1d[0][:, 2], exact_basis_derivs_1d[0][:, 1] * exact_basis_vals_1d[1][:, 1], 0. * x ]).T)) # x2 derivative exact_basis_matrix = np.vstack( (exact_basis_matrix, np.asarray([ 0. * x, 0. * x, exact_basis_derivs_1d[1][:, 1], 0. * x, exact_basis_vals_1d[0][:, 1] * exact_basis_derivs_1d[1][:, 1], exact_basis_derivs_1d[1][:, 2] ]).T)) def func(x): return evaluate_multivariate_orthonormal_polynomial( x, indices, ab, 0) basis_matrix_derivs = basis_matrix[samples.shape[1]:] basis_matrix_derivs_fd = np.empty_like(basis_matrix_derivs) for ii in range(samples.shape[1]): basis_matrix_derivs_fd[ii::samples.shape[1], :] = approx_fprime( samples[:, ii:ii + 1], func, 1e-7) assert np.allclose(exact_basis_matrix[samples.shape[1]:], basis_matrix_derivs_fd) assert np.allclose(exact_basis_matrix, basis_matrix)
def test_multivariate_barycentric_lagrange_interpolation(self): def f(x): return np.sum(x**2, axis=0) eps = 1e-14 # test 1d barycentric lagrange interpolation level = 5 #abscissa, __ = clenshaw_curtis_pts_wts_1D( level ) #barycentric_weights_1d = [clenshaw_curtis_barycentric_weights(level)] abscissa, __ = clenshaw_curtis_in_polynomial_order(level, False) abscissa_1d = [abscissa] barycentric_weights_1d = [ compute_barycentric_weights_1d(abscissa_1d[0]) ] fn_vals = f(np.array(abscissa).reshape(1, abscissa.shape[0]))[:, np.newaxis] pts = np.linspace(-1., 1., 3).reshape(1, 3) poly_vals = multivariate_barycentric_lagrange_interpolation( pts, abscissa_1d, barycentric_weights_1d, fn_vals, np.array([0])) #import pylab # print poly_vals.squeeze().shape # pylab.plot(pts[0,:],poly_vals.squeeze()) # pylab.plot(abscissa_1d[0],fn_vals.squeeze(),'ro') # print np.linalg.norm( poly_vals - f( pts ) ) # pylab.show() assert np.allclose(poly_vals, f(pts)[:, np.newaxis], eps) # test 2d barycentric lagrange interpolation # with the same abscissa in each dimension a = -3.0 b = 3.0 x = np.linspace(a, b, 21) [X, Y] = np.meshgrid(x, x) pts = np.vstack((X.reshape((1, X.shape[0] * X.shape[1])), Y.reshape((1, Y.shape[0] * Y.shape[1])))) num_abscissa = [10, 10] abscissa_1d = [ np.linspace(a, b, num_abscissa[0]), np.linspace(a, b, num_abscissa[1]) ] abscissa = cartesian_product(abscissa_1d, 1) fn_vals = f(abscissa) barycentric_weights_1d = [ compute_barycentric_weights_1d(abscissa_1d[0]), compute_barycentric_weights_1d(abscissa_1d[1]) ] poly_vals = multivariate_barycentric_lagrange_interpolation( pts, abscissa_1d, barycentric_weights_1d, fn_vals[:, np.newaxis], np.array([0, 1])) assert np.allclose(poly_vals, f(pts)[:, np.newaxis], eps) # test 2d barycentric lagrange interpolation # with different abscissa in each dimension a = -1.0 b = 1.0 x = np.linspace(a, b, 21) [X, Y] = np.meshgrid(x, x) pts = np.vstack((X.reshape((1, X.shape[0] * X.shape[1])), Y.reshape((1, Y.shape[0] * Y.shape[1])))) level = [1, 2] nodes_0, tmp = clenshaw_curtis_pts_wts_1D(level[0]) nodes_1, tmp = clenshaw_curtis_pts_wts_1D(level[1]) abscissa_1d = [nodes_0, nodes_1] barycentric_weights_1d = [ clenshaw_curtis_barycentric_weights(level[0]), clenshaw_curtis_barycentric_weights(level[1]) ] abscissa = cartesian_product(abscissa_1d, 1) fn_vals = f(abscissa) poly_vals = multivariate_barycentric_lagrange_interpolation( pts, abscissa_1d, barycentric_weights_1d, fn_vals[:, np.newaxis], np.array([0, 1])) assert np.allclose(poly_vals, f(pts)[:, np.newaxis], eps) # test 3d barycentric lagrange interpolation # with different abscissa in each dimension num_dims = 3 a = -1.0 b = 1.0 pts = np.random.uniform(-1., 1., (num_dims, 10)) level = [1, 1, 1] nodes_0, tmp = clenshaw_curtis_pts_wts_1D(level[0]) nodes_1, tmp = clenshaw_curtis_pts_wts_1D(level[1]) nodes_2, tmp = clenshaw_curtis_pts_wts_1D(level[2]) abscissa_1d = [nodes_0, nodes_1, nodes_2] barycentric_weights_1d = [ clenshaw_curtis_barycentric_weights(level[0]), clenshaw_curtis_barycentric_weights(level[1]), clenshaw_curtis_barycentric_weights(level[2]) ] abscissa = cartesian_product(abscissa_1d, 1) fn_vals = f(abscissa) poly_vals = multivariate_barycentric_lagrange_interpolation( pts, abscissa_1d, barycentric_weights_1d, fn_vals[:, np.newaxis], np.array([0, 1, 2])) assert np.allclose(poly_vals, f(pts)[:, np.newaxis], eps) # test 3d barycentric lagrange interpolation # with different abscissa in each dimension # and only two active dimensions (0 and 2) num_dims = 3 a = -1.0 b = 1.0 pts = np.random.uniform(-1., 1., (num_dims, 5)) level = [2, 0, 1] # to get fn_vals we must specify abscissa for all three dimensions # but only the abscissa of the active dimensions should get passed # to the interpolation function nodes_0, tmp = clenshaw_curtis_pts_wts_1D(level[0]) nodes_1, tmp = clenshaw_curtis_pts_wts_1D(level[1]) nodes_2, tmp = clenshaw_curtis_pts_wts_1D(level[2]) abscissa_1d = [nodes_0, nodes_1, nodes_2] abscissa = cartesian_product(abscissa_1d, 1) abscissa_1d = [nodes_0, nodes_2] barycentric_weights_1d = [ clenshaw_curtis_barycentric_weights(level[0]), clenshaw_curtis_barycentric_weights(level[2]) ] fn_vals = f(abscissa) poly_vals = multivariate_barycentric_lagrange_interpolation( pts, abscissa_1d, barycentric_weights_1d, fn_vals[:, np.newaxis], np.array([0, 2])) pts[1, :] = 0. assert np.allclose(poly_vals, f(pts)[:, np.newaxis], eps) # test 3d barycentric lagrange interpolation # with different abscissa in each dimension # and only two active dimensions (0 and 1) num_dims = 3 a = -1.0 b = 1.0 pts = np.random.uniform(-1., 1., (num_dims, 5)) level = [2, 3, 0] # to get fn_vals we must specify abscissa for all three dimensions # but only the abscissa of the active dimensions should get passed # to the interpolation function nodes_0, tmp = clenshaw_curtis_pts_wts_1D(level[0]) nodes_1, tmp = clenshaw_curtis_pts_wts_1D(level[1]) nodes_2, tmp = clenshaw_curtis_pts_wts_1D(level[2]) abscissa_1d = [nodes_0, nodes_1, nodes_2] abscissa = cartesian_product(abscissa_1d, 1) abscissa_1d = [nodes_0, nodes_1] barycentric_weights_1d = [ clenshaw_curtis_barycentric_weights(level[0]), clenshaw_curtis_barycentric_weights(level[1]) ] fn_vals = f(abscissa) poly_vals = multivariate_barycentric_lagrange_interpolation( pts, abscissa_1d, barycentric_weights_1d, fn_vals[:, np.newaxis], np.array([0, 1])) # The interpolant will only be correct on the plane involving # the active dimensions so we must set the coordinate of the inactive # dimension to the abscissa coordinate of the inactive dimension. # The interpoolation algorithm is efficient in the sense that it # ignores all dimensions involving only one point because the # interpolant will be a constant in that direction pts[2, :] = 0. assert np.allclose(poly_vals, f(pts)[:, np.newaxis], eps) # test 3d barycentric lagrange interpolation # with different abscissa in each dimension # and only two active dimensions (1 and 2) num_dims = 3 a = -1.0 b = 1.0 pts = np.random.uniform(-1., 1., (num_dims, 5)) level = [0, 2, 4] # to get fn_vals we must specify abscissa for all three dimensions # but only the abscissa of the active dimensions should get passed # to the interpolation function nodes_0, tmp = clenshaw_curtis_pts_wts_1D(level[0]) nodes_1, tmp = clenshaw_curtis_pts_wts_1D(level[1]) nodes_2, tmp = clenshaw_curtis_pts_wts_1D(level[2]) abscissa_1d = [nodes_0, nodes_1, nodes_2] abscissa = cartesian_product(abscissa_1d, 1) abscissa_1d = [nodes_1, nodes_2] barycentric_weights_1d = [ clenshaw_curtis_barycentric_weights(level[1]), clenshaw_curtis_barycentric_weights(level[2]) ] fn_vals = f(abscissa) poly_vals = multivariate_barycentric_lagrange_interpolation( pts, abscissa_1d, barycentric_weights_1d, fn_vals[:, np.newaxis], np.array([1, 2])) pts[0, :] = 0. assert np.allclose(poly_vals, f(pts)[:, np.newaxis], eps) # test 2d barycentric lagrange interpolation # with different abscissa in each dimension and only some of # the coefficients of the basis terms being non-zero. This situation # arises in hierarchical interpolation. In these cases we need # to construct the basis functions on all abscissa but we only # need to add the basis functions that are one at the hierachical # nodes a = -1.0 b = 1.0 #x = np.linspace( a, b, 21 ) x = np.linspace(a, b, 5) [X, Y] = np.meshgrid(x, x) pts = np.vstack((X.reshape((1, X.shape[0] * X.shape[1])), Y.reshape((1, Y.shape[0] * Y.shape[1])))) poly_vals = np.ones((pts.shape[1], 1), np.double) * \ f(np.array([[0.0, 0.0]]).T)[:, np.newaxis] level = [1] nodes_0, tmp = clenshaw_curtis_pts_wts_1D(level[0]) abscissa_1d = [nodes_0] barycentric_weights_1d = [ compute_barycentric_weights_1d(abscissa_1d[0]) ] sets = copy.copy(abscissa_1d) sets.append(np.array([0.0])) abscissa = cartesian_product(sets, 1) hier_indices = np.array([[0, 2]], np.int32) abscissa = abscissa[:, hier_indices[0]] fn_vals = f(abscissa) poly_vals_increment = \ multivariate_hierarchical_barycentric_lagrange_interpolation( pts, abscissa_1d, barycentric_weights_1d, (fn_vals - np.ones((abscissa.shape[1]), np.double) * f( np.array([0.0, 0.0])))[:, np.newaxis], np.array([0]), hier_indices) poly_vals += poly_vals_increment level = [1] nodes_0, tmp = clenshaw_curtis_pts_wts_1D(level[0]) abscissa_1d = [nodes_0] # barycentric_weights_1d = [barycentric_weights( np.array( [0.0] ) ), # barycentric_weights( abscissa_1d[0] )] barycentric_weights_1d = [ compute_barycentric_weights_1d(abscissa_1d[0]) ] sets = [np.array([0.0])] sets.append(nodes_0) abscissa = cartesian_product(sets, 1) hier_indices = np.array([[0, 2]], np.int32) abscissa = abscissa[:, hier_indices[0]] fn_vals = f(abscissa) poly_vals += \ multivariate_hierarchical_barycentric_lagrange_interpolation( pts, abscissa_1d, barycentric_weights_1d, (fn_vals - np.ones((abscissa.shape[1]), np.double) * f( np.array([[0.0, 0.0]]).T))[:, np.newaxis], np.array([1]), hier_indices) assert np.allclose(poly_vals, f(pts)[:, np.newaxis], eps)
def help_discrete_induced_sampling(self, var1, var2, envelope_factor): degree = 3 var_trans = AffineRandomVariableTransformation([var1, var2]) pce_opts = define_poly_options_from_variable_transformation(var_trans) pce = PolynomialChaosExpansion() pce.configure(pce_opts) indices = compute_hyperbolic_indices(pce.num_vars(), degree, 1.0) pce.set_indices(indices) num_samples = int(3e4) np.random.seed(1) canonical_samples = generate_induced_samples(pce, num_samples) samples = var_trans.map_from_canonical_space(canonical_samples) np.random.seed(1) #canonical_xk = [2*get_distribution_info(var1)[2]['xk']-1, # 2*get_distribution_info(var2)[2]['xk']-1] xk = np.array([ get_probability_masses(var)[0] for var in var_trans.variable.all_variables() ]) pk = np.array([ get_probability_masses(var)[1] for var in var_trans.variable.all_variables() ]) canonical_xk = var_trans.map_to_canonical_space(xk) basis_matrix_generator = partial(basis_matrix_generator_1d, pce, degree) canonical_samples1 = discrete_induced_sampling(basis_matrix_generator, pce.indices, canonical_xk, pk, num_samples) samples1 = var_trans.map_from_canonical_space(canonical_samples1) def univariate_pdf(var, x): if hasattr(var.dist, 'pdf'): return var.pdf(x) else: return var.pmf(x) xk, pk = get_probability_masses(var) x = np.atleast_1d(x) vals = np.zeros(x.shape[0]) for jj in range(x.shape[0]): for ii in range(xk.shape[0]): if xk[ii] == x[jj]: vals[jj] = pk[ii] break return vals def density(x): # some issue with native scipy.pmf #assert np.allclose(var1.pdf(x[0, :]),var1.pmf(x[0, :])) return univariate_pdf(var1, x[0, :]) * univariate_pdf( var2, x[1, :]) def generate_proposal_samples(n): samples = np.vstack([var1.rvs(n), var2.rvs(n)]) return samples proposal_density = density # unlike fekete and leja sampling can and should use # pce.basis_matrix here. If use canonical_basis_matrix then # densities must be mapped to this space also which can be difficult samples2 = random_induced_measure_sampling(num_samples, pce.num_vars(), pce.basis_matrix, density, proposal_density, generate_proposal_samples, envelope_factor) def induced_density(x): vals = density(x) * christoffel_function(x, pce.basis_matrix, True) return vals from pyapprox.utilities import cartesian_product, outer_product from pyapprox.polynomial_sampling import christoffel_function quad_samples = cartesian_product([xk[0], xk[1]]) quad_weights = outer_product([pk[0], pk[1]]) # print(canonical_samples.min(axis=1),canonical_samples.max(axis=1)) # print(samples.min(axis=1),samples.max(axis=1)) # print(canonical_samples1.min(axis=1),canonical_samples1.max(axis=1)) # print(samples1.min(axis=1),samples1.max(axis=1)) # import matplotlib.pyplot as plt # plt.plot(quad_samples[0,:],quad_samples[1,:],'s') # plt.plot(samples[0,:],samples[1,:],'o') # plt.plot(samples1[0,:],samples1[1,:],'*') # plt.show() rtol = 1e-2 assert np.allclose(quad_weights, density(quad_samples)) assert np.allclose(density(quad_samples).sum(), 1) assert np.allclose( christoffel_function(quad_samples, pce.basis_matrix, True).dot(quad_weights), 1.0) true_induced_mean = quad_samples.dot(induced_density(quad_samples)) # print(true_induced_mean) # print(samples.mean(axis=1)) # print(samples1.mean(axis=1)) # print(samples2.mean(axis=1)) # print(samples1.mean(axis=1)-true_induced_mean, true_induced_mean*rtol) # print(samples2.mean(axis=1)) assert np.allclose(samples.mean(axis=1), true_induced_mean, rtol=rtol) assert np.allclose(samples1.mean(axis=1), true_induced_mean, rtol=rtol) assert np.allclose(samples2.mean(axis=1), true_induced_mean, rtol=rtol)
def test_pce_product_of_beta_variables(self): def fun(x): return np.sqrt(x.prod(axis=0))[:, None] dist_alpha1, dist_beta1 = 1, 1 dist_alpha2, dist_beta2 = dist_alpha1 + 0.5, dist_beta1 nvars = 2 x_1d, w_1d = [], [] nquad_samples_1d = 100 x, w = gauss_jacobi_pts_wts_1D(nquad_samples_1d, dist_beta1 - 1, dist_alpha1 - 1) x = (x + 1) / 2 x_1d.append(x) w_1d.append(w) x, w = gauss_jacobi_pts_wts_1D(nquad_samples_1d, dist_beta2 - 1, dist_alpha2 - 1) x = (x + 1) / 2 x_1d.append(x) w_1d.append(w) quad_samples = cartesian_product(x_1d) quad_weights = outer_product(w_1d) mean = fun(quad_samples)[:, 0].dot(quad_weights) variance = (fun(quad_samples)[:, 0]**2).dot(quad_weights) - mean**2 assert np.allclose(mean, stats.beta(dist_alpha1 * 2, dist_beta1 * 2).mean()) assert np.allclose(variance, stats.beta(dist_alpha1 * 2, dist_beta1 * 2).var()) degree = 10 poly = PolynomialChaosExpansion() # the distribution and ranges of univariate variables is ignored # when var_trans.set_identity_maps([0]) is used initial_variables = [stats.uniform(0, 1)] # TODO get quad rules from initial variables quad_rules = [(x, w) for x, w in zip(x_1d, w_1d)] univariate_variables = [ rv_function_indpndt_vars(fun, initial_variables, quad_rules) ] variable = IndependentMultivariateRandomVariable(univariate_variables) var_trans = AffineRandomVariableTransformation(variable) poly_opts = define_poly_options_from_variable_transformation(var_trans) poly.configure(poly_opts) poly.set_indices(tensor_product_indices([degree])) train_samples = (np.linspace(0, np.pi, 101)[None, :] + 1) / 2 train_vals = train_samples.T coef = np.linalg.lstsq(poly.basis_matrix(train_samples), train_vals, rcond=None)[0] poly.set_coefficients(coef) assert np.allclose(poly.mean(), stats.beta(dist_alpha1 * 2, dist_beta1 * 2).mean()) assert np.allclose(poly.variance(), stats.beta(dist_alpha1 * 2, dist_beta1 * 2).var()) poly = PolynomialChaosExpansion() initial_variables = [stats.uniform(0, 1)] funs = [lambda x: np.sqrt(x)] * nvars quad_rules = [(x, w) for x, w in zip(x_1d, w_1d)] # TODO get quad rules from initial variables univariate_variables = [ rv_product_indpndt_vars(funs, initial_variables, quad_rules) ] variable = IndependentMultivariateRandomVariable(univariate_variables) var_trans = AffineRandomVariableTransformation(variable) poly_opts = define_poly_options_from_variable_transformation(var_trans) poly.configure(poly_opts) poly.set_indices(tensor_product_indices([degree])) train_samples = (np.linspace(0, np.pi, 101)[None, :] + 1) / 2 train_vals = train_samples.T coef = np.linalg.lstsq(poly.basis_matrix(train_samples), train_vals, rcond=None)[0] poly.set_coefficients(coef) assert np.allclose(poly.mean(), stats.beta(dist_alpha1 * 2, dist_beta1 * 2).mean()) assert np.allclose(poly.variance(), stats.beta(dist_alpha1 * 2, dist_beta1 * 2).var())