def get_univariate_leja_quadrature_rule( variable, growth_rule, method='pdf', numerically_generated_poly_accuracy_tolerance=1e-12, initial_points=None): if not is_continuous_variable(variable): return get_discrete_univariate_leja_quadrature_rule( variable, growth_rule, numerically_generated_poly_accuracy_tolerance=numerically_generated_poly_accuracy_tolerance, initial_points=initial_points) if method == 'christoffel': return partial( univariate_christoffel_leja_quadrature_rule, variable, growth_rule, numerically_generated_poly_accuracy_tolerance=numerically_generated_poly_accuracy_tolerance, initial_points=initial_points) if method == 'pdf': return partial( univariate_pdf_weighted_leja_quadrature_rule, variable, growth_rule, numerically_generated_poly_accuracy_tolerance=numerically_generated_poly_accuracy_tolerance, initial_points=initial_points) assert method == 'deprecated' var_name, __, shapes = get_distribution_info(variable) if var_name == 'uniform': quad_rule = partial( beta_leja_quadrature_rule, 1, 1, growth_rule=growth_rule, samples_filename=None) elif var_name == 'beta': quad_rule = partial( beta_leja_quadrature_rule, shapes['a'], shapes['b'], growth_rule=growth_rule) elif var_name == 'norm': quad_rule = partial( gaussian_leja_quadrature_rule, growth_rule=growth_rule) else: raise Exception('var_name %s not implemented' % var_name) return quad_rule
def get_univariate_leja_quadrature_rule(variable, growth_rule, method='pdf', orthonormality_tol=1e-8, initial_points=None, return_weights_for_all_levels=True, recursion_opts=None, minimizer_opts=None): if not is_continuous_variable(variable): return get_discrete_univariate_leja_quadrature_rule( variable, growth_rule, orthonormality_tol=orthonormality_tol, initial_points=initial_points, return_weights_for_all_levels=return_weights_for_all_levels, recursion_opts=recursion_opts) if method == 'christoffel': return partial( univariate_christoffel_leja_quadrature_rule, variable, growth_rule, orthonormality_tol=orthonormality_tol, initial_points=initial_points, return_weights_for_all_levels=return_weights_for_all_levels, recursion_opts=recursion_opts, minimizer_opts=minimizer_opts) if method == 'pdf': return partial( univariate_pdf_weighted_leja_quadrature_rule, variable, growth_rule, orthonormality_tol=orthonormality_tol, initial_points=initial_points, return_weights_for_all_levels=return_weights_for_all_levels, recursion_opts=recursion_opts, minimizer_opts=minimizer_opts) raise ValueError(f"Method {method} not supported")
def univariate_christoffel_leja_quadrature_rule( variable, growth_rule, level, return_weights_for_all_levels=True, initial_points=None, orthonormality_tol=1e-12, recursion_opts=None, minimizer_opts=None): """ Return the samples and weights of the Leja quadrature rule for any continuous variable using the inverse Christoffel weight function By construction these rules have polynomial ordering. Parameters ---------- variable : scipy.stats.dist The variable used to construct an orthogonormal polynomial growth_rule : callable Function which returns the number of samples in the quadrature rule With signature `growth_rule(level) -> integer` where level is an integer level : integer The level of the univariate rule. return_weights_for_all_levels : boolean True - return weights [w(0),w(1),...,w(level)] False - return w(level) initial_points : np.ndarray (1, ninit_samples) Any points that must be included in the Leja sequence. This argument is typically used to pass in previously computed sequence which is updated efficiently here. MUST be in the canonical domain Return ------ ordered_samples_1d : np.ndarray (num_samples_1d) The reordered samples. ordered_weights_1d : np.ndarray (num_samples_1d) The reordered weights. """ if not is_continuous_variable(variable): raise Exception('Only supports continuous variables') name, scales, shapes = get_distribution_info(variable) max_nsamples = growth_rule(level) if recursion_opts is None: recursion_opts = {"orthonormality_tol": orthonormality_tol} ab = get_recursion_coefficients_from_variable(variable, max_nsamples + 1, recursion_opts) basis_fun = partial(evaluate_orthonormal_polynomial_deriv_1d, ab=ab) initial_points, bounds = transform_initial_samples(variable, initial_points) if minimizer_opts is None: minimizer_opts = {'gtol': 1e-8, 'verbose': False} if ("artificial_bounds" not in minimizer_opts and not is_bounded_continuous_variable(variable)): # make bounds twice that of gauss quadrature points xg, wg = gauss_quadrature(ab, max_nsamples) artificial_bounds = bounds.copy() if not np.isfinite(bounds[0]): artificial_bounds[0] = xg.min() artificial_bounds[0] = artificial_bounds[0] - abs( artificial_bounds[0]) if not np.isfinite(bounds[1]): artificial_bounds[1] = xg.max() artificial_bounds[1] = artificial_bounds[1] + abs( artificial_bounds[1]) minimizer_opts["artificial_bounds"] = artificial_bounds leja_sequence = get_christoffel_leja_sequence_1d(max_nsamples, initial_points, bounds, basis_fun, minimizer_opts, callback=None) __basis_fun = partial(basis_fun, nmax=max_nsamples - 1, deriv_order=0) ordered_weights_1d = get_christoffel_leja_quadrature_weights_1d( leja_sequence, growth_rule, __basis_fun, level, True) if return_weights_for_all_levels: return leja_sequence[0, :], ordered_weights_1d return leja_sequence[0, :], ordered_weights_1d[-1]
def univariate_pdf_weighted_leja_quadrature_rule( variable, growth_rule, level, return_weights_for_all_levels=True, initial_points=None, numerically_generated_poly_accuracy_tolerance=1e-12): """ Return the samples and weights of the Leja quadrature rule for any continuous variable using the PDF of the random variable as the weight function By construction these rules have polynomial ordering. Parameters ---------- variable : scipy.stats.dist The variable used to construct an orthogonormal polynomial growth_rule : callable Function which returns the number of samples in the quadrature rule With signature `growth_rule(level) -> integer` where level is an integer level : integer The level of the univariate rule. return_weights_for_all_levels : boolean True - return weights [w(0),w(1),...,w(level)] False - return w(level) initial_points : np.ndarray (1, ninit_samples) Any points that must be included in the Leja sequence. This argument is typically used to pass in previously computed sequence which is updated efficiently here. Return ------ ordered_samples_1d : np.ndarray (num_samples_1d) The reordered samples. ordered_weights_1d : np.ndarray (num_samples_1d) The reordered weights. """ if not is_continuous_variable(variable): raise Exception('Only supports continuous variables') name, scales, shapes = get_distribution_info(variable) opts = {'rv_type': name, 'shapes': shapes, 'var_nums': variable} max_nsamples = growth_rule(level) ab = get_recursion_coefficients( opts, max_nsamples+1, numerically_generated_poly_accuracy_tolerance) basis_fun = partial(evaluate_orthonormal_polynomial_deriv_1d, ab=ab) pdf, pdf_jac = get_pdf_weight_functions(variable) if is_bounded_continuous_variable(variable): bounds = variable.interval(1.) canonical_bounds = [-1, 1] if initial_points is None: initial_points = np.asarray( [[variable.ppf(0.5)]]).T loc, scale = scales['loc'], scales['scale'] lb, ub = -1, 1 scale /= (ub-lb) loc = loc-scale*lb initial_points = (initial_points-loc)/scale assert np.all((initial_points >= canonical_bounds[0]) & (initial_points <= canonical_bounds[1])) # always produce sequence in canonical space bounds = canonical_bounds else: bounds = list(variable.interval(1)) if initial_points is None: initial_points = np.asarray( [[variable.ppf(0.5)]]).T loc, scale = scales['loc'], scales['scale'] initial_points = (initial_points-loc)/scale leja_sequence = get_pdf_weighted_leja_sequence_1d( max_nsamples, initial_points, bounds, basis_fun, pdf, pdf_jac, {'gtol': 1e-8, 'verbose': False}, callback=None) __basis_fun = partial(basis_fun, nmax=max_nsamples-1, deriv_order=0) ordered_weights_1d = get_pdf_weighted_leja_quadrature_weights_1d( leja_sequence, growth_rule, pdf, __basis_fun, level, True) return leja_sequence[0, :], ordered_weights_1d
def get_recursion_coefficients_from_variable(var, num_coefs, opts): """ Generate polynomial recursion coefficients by inspecting a random variable. """ var_name, _, shapes = get_distribution_info(var) if var_name == "continuous_monomial": return None loc, scale = transform_scale_parameters(var) if var_name == "rv_function_indpndt_vars": shapes["loc"] = loc shapes["scale"] = scale return get_function_independent_vars_recursion_coefficients( shapes, num_coefs) if var_name == "rv_product_indpndt_vars": shapes["loc"] = loc shapes["scale"] = scale return get_product_independent_vars_recursion_coefficients( shapes, num_coefs) if (var_name in askey_variable_names and opts.get("numeric", False) is False): return get_askey_recursion_coefficients_from_variable(var, num_coefs) orthonormality_tol = opts.get("orthonormality_tol", 1e-8) truncated_probability_tol = opts.get("truncated_probability_tol", 0) if (not is_continuous_variable(var)): if hasattr(shapes, "xk"): xk, pk = shapes["xk"], shapes["pk"] else: xk, pk = get_probability_masses(var, truncated_probability_tol) xk = (xk - loc) / scale return get_numerically_generated_recursion_coefficients_from_samples( xk, pk, num_coefs, orthonormality_tol, truncated_probability_tol) # integration performed in canonical domain so need to map back to # domain of pdf lb, ub = var.interval(1) # Get version var.pdf without error checking which runs much faster pdf = get_pdf(var) def canonical_pdf(x): # print(x, lb, ub, x*scale+loc) # print(var.pdf(x*scale+loc)*scale) # assert np.all(x*scale+loc >= lb) and np.all(x*scale+loc <= ub) return pdf(x * scale + loc) * scale # return var.pdf(x*scale+loc)*scale if (is_bounded_continuous_variable(var) or is_bounded_discrete_variable(var)): can_lb, can_ub = -1, 1 elif is_continuous_variable(var): can_lb = (lb - loc) / scale can_ub = (ub - loc) / scale return predictor_corrector_known_pdf(num_coefs, can_lb, can_ub, canonical_pdf, opts)