def testSparseMonomialQuadrature(self): chaos = make_legendre() dim = 2 level_max = 3 # legendre and hermite: level,maxsumbound: 1,3/2,7/3,11/4,17/5,31/6,78 sum_bound = 11 # open weakly nested order is 2 ** (level+1) - 1 (for level=3 this is 15) quad = SparseQuadrature(level_max, nst.get_nesting_for_name(chaos.name), dim * [chaos.nodes_and_weights]) for multi_index in multi_index_bounded_sum(dim, sum_bound): def func(xs): return mul_prod(x**index for x, index in zip(xs, multi_index)) result = quad.apply(func) if chaos.name == "Legendre": wanted = (0 if any(index % 2 == 1 for index in multi_index) else mul_prod( 1. / (index + 1) for index in multi_index)) elif chaos.name == "Hermite": def uneven_factorial(number): fac = 1 while number > 1: fac *= number - 1 number -= 2 return fac wanted = (0 if any(index % 2 == 1 for index in multi_index) else mul_prod( uneven_factorial(index) for index in multi_index)) # print(result, wanted, "for multi index", multi_index) self.assertAlmostEqual(result, wanted)
def __init__(self, nodes_and_weights_funcs, poly_names, sum_bound): nodes_list, weights_list = [], [] length = sum_bound + 1 nodes_weights_pairs = [nodes_and_weights_func(length) for nodes_and_weights_func in nodes_and_weights_funcs] # for every multi index we add one nodes tuple to the list, so we will later have the same # amount of nodes/weights as we have basis polynomials with the same sum_bound indices = multi_index_bounded_sum(len(nodes_and_weights_funcs), sum_bound) for multi_index in indices: current_nodes, current_weights = [], [] for (nodes, weights), index, poly_name in zip(nodes_weights_pairs, multi_index, poly_names): # here it is important that we have enough nodes to use the multi_index's index! if poly_name in ["Hermite", "Legendre"]: index = centralize_index(index, length) # important as nodes are symmetric around the center elif poly_name == "Jacobi": index = length - index - 1 elif poly_name == "Laguerre": pass # do not change index else: raise ValueError("Not supported polynomials:", poly_name) current_nodes.append(nodes[index]) current_weights.append(weights[index]) nodes_list.append(current_nodes) weights_list.append(mul_prod(current_weights)) self.nodes = np.array(nodes_list) self.weights = np.array(weights_list)
def func_in_ys(*ys): transformed_ys = [ rvar(value) for rvar, value in zip(self.rvars, ys) ] return (function(x_coords, t, transformed_ys) * mul_prod( distr.weight(y) for y, distr in zip(ys, self.variable_distributions)))
def sparse_grid(dim_num: int, level_min2: int, level_max: int, point_num: int, nodes_and_weights_funcs, calculate_grid_base2, calculate_offset, calculate_index_level): grid_weight = np.zeros(point_num) grid_point = np.zeros((dim_num, point_num)) point_num2 = 0 level_min = max(0, level_max + 1 - dim_num) last_point3 = -1 for level in range(level_min2, level_max + 1): for level_1d in compositions(level, dim_num): order_1d = level_to_order_open(level_1d) grid_base2 = calculate_grid_base2(order_1d) order_nd = mul_prod(order_1d) grid_weights2 = product_weights(dim_num, order_1d, order_nd, nodes_and_weights_funcs) coeff = ((-1)**((level_max - level) % 2)) * comb( dim_num - 1, level_max - level, exact=True) offset = calculate_offset(order_1d) grid_index2 = multigrid_index(dim_num, order_1d, order_nd, offset) # level is used to find out if the point is new or already existed as it sets the lowest level # of appearance of each point. If grid_level is None then every point is new grid_level = calculate_index_level(level, level_max, dim_num, order_nd, grid_index2, grid_base2) for point in range(order_nd): if grid_level is None or grid_level[point] == level: # new point! point_num2 += 1 assert point_num2 <= point_num grid_point[:, point_num2 - 1] = multigrid_point( dim_num, grid_index2[:, point], order_1d, offset, nodes_and_weights_funcs) if level_min <= level: grid_weight[point_num2 - 1] = coeff * grid_weights2[point] elif level_min <= level: # already existing point! grid_point_temp = multigrid_point(dim_num, grid_index2[:, point], order_1d, offset, nodes_and_weights_funcs) # find the index point3 = -1 # it is very common that the point is the last_point3+1, so instead of using # bruteforce search range(point_num2) we make the order smarter # this SIGNIFICANTLY improves speed, as np.allclose is in the innermost loop and quite costly for point2 in chain([last_point3 + 1], range(last_point3 + 1), range(last_point3 + 2, point_num2)): if np.allclose(grid_point[:, point2], grid_point_temp): point3 = point2 last_point3 = point3 break assert point3 != -1 # we know it has to be somewhere as grid_level[point] != level grid_weight[point3] += coeff * grid_weights2[point] assert point_num2 == point_num return np.rollaxis(grid_point, 1), grid_weight
def calculate_point_num(dim_num: int, level_max: int): if level_max == 0: return 1 # the amount of new nodes appearing per level, so 1 new for level=0, 2 new for level=1, afterwards 2^(level-1) new new_1d = np.array([1, 2] + [2**i for i in range(1, level_max)]) point_num = 0 for level in range(level_max + 1): for level_1d in compositions(level, dim_num): point_num += mul_prod(new_1d[level_1d]) return point_num
def __init__(self, orders_1d, nodes_and_weights_funcs): assert len(orders_1d) == len(nodes_and_weights_funcs) # contains [([n11,n12,n13],[w11,w12,w13]), ([n21,n22],[w21,w22])] nodes_weights_pairs = [nodes_and_weights(order) for order, nodes_and_weights in zip(orders_1d, nodes_and_weights_funcs)] nodes_list = [nodes for nodes, _ in nodes_weights_pairs] weights_list = [weights for _, weights in nodes_weights_pairs] # use full tensor product of all dimensions by using 'product' self.nodes = np.array([grid_nodes for grid_nodes in product(*nodes_list)]) self.weights = np.array([mul_prod(grid_weights) for grid_weights in product(*weights_list)])
def calculate_point_num(dim_num: int, level_min: int, level_max: int, init_point_num: int, repetition_handler=None): if level_max == 0: return 1 point_num = init_point_num for level in range(level_min, level_max + 1): for level_1d in compositions(level, dim_num): order_1d = level_to_order_open(level_1d) if repetition_handler is not None: order_1d = repetition_handler(order_1d) point_num += mul_prod( order_1d ) # the number of points is the full product of all 1d points return point_num
def levels_index(dim_num: int, level_max: int, point_num: int): grid_index = np.zeros((dim_num, point_num), dtype=int) grid_base = np.zeros((dim_num, point_num), dtype=int) point_num2 = 0 for level in range(level_max + 1): for level_1d in compositions(level, dim_num): order_1d = level_to_order_closed(level_1d) order_nd = mul_prod(order_1d) grid_index2 = multigrid_index(dim_num, order_1d, order_nd) prev_shape = grid_index2.shape grid_index2 = multigrid_scale_closed(dim_num, level_max, level_1d, grid_index2) grid_level = abscissa_level_closed_nd(level_max, dim_num, order_nd, grid_index2) for point in range(order_nd): if grid_level[point] == level: point_num2 += 1 assert point_num2 <= point_num grid_base[:, point_num2 - 1] = order_1d grid_index[:, point_num2 - 1] = grid_index2[:, point] assert point_num2 == point_num return grid_index, grid_base
def __init__(self, nodes_and_weights_funcs, sum_bound, even): nodes_list, weights_list = [], [] length = sum_bound + 1 # ignore lengths and use sum_bound+1 for every dimension to ensure we can index! nodes_weights_pairs = [nodes_and_weights_func(length) for nodes_and_weights_func in nodes_and_weights_funcs] # for every multi index we add one nodes tuple to the list, so we will later have the same # amount of nodes/weights as we have basis polynomials. if even: indices = list(multi_index_bounded_sum(len(nodes_and_weights_funcs), sum_bound + 1 if sum_bound % 2 == 1 else sum_bound)) else: indices = multi_index_bounded_sum(len(nodes_and_weights_funcs), sum_bound) for multi_index in indices: current_nodes, current_weights = [], [] for (nodes, weights), index in zip(nodes_weights_pairs, multi_index): # here it is important that we have enough nodes to use the multi_index's index! centralized = centralize_index(index, length) # important as nodes are symmetric around the center current_nodes.append(nodes[centralized]) current_weights.append(weights[centralized]) nodes_list.append(current_nodes) weights_list.append(mul_prod(current_weights)) self.nodes = np.array(nodes_list) self.weights = np.array(weights_list)
def sparse_grid_weights(dim_num: int, level_max: int, point_num: int, grid_index, nodes_and_weights_funcs): grid_weight = np.zeros(point_num) level_min = max(0, level_max + 1 - dim_num) for level in range(level_min, level_max + 1): for level_1d in compositions(level, dim_num): order_1d = level_to_order_closed(level_1d) order_nd = mul_prod(order_1d) grid_index2 = multigrid_index(dim_num, order_1d, order_nd) grid_weight2 = product_weights(dim_num, order_1d, order_nd, nodes_and_weights_funcs) grid_index2 = multigrid_scale_closed(dim_num, level_max, level_1d, grid_index2) coeff = ((-1)**((level_max - level) % 2)) * comb( dim_num - 1, level_max - level, exact=True) for point2 in range(order_nd): for point in range(point_num): if np.all(grid_index2[:, point2] == grid_index[:, point]): grid_weight[point] += coeff * grid_weight2[point2] break return grid_weight
def func(xs): return mul_prod(x**index for x, index in zip(xs, multi_index))
def testGlenshawCurtisQuadrature(self): import util.quadrature.glenshaw_curtis as gc nodes_and_weights = gc.nodes_and_weights from util.quadrature.closed_fully_nested import ClosedFullNesting nesting = ClosedFullNesting() # 1d polynomial exactness count = 10 # exact if polynomials are degree < count quad = FullTensorQuadrature([count], [nodes_and_weights]) for n in range(count): result = quad.apply(lambda xs: xs[0]**n) wanted = 2. / (n + 1) if n % 2 == 0 else 0. self.assertAlmostEqual( result, wanted, places=10, msg="GC quad with count = {}, n={} polynomial exactness". format(count, n)) # 2d test count = 8 # exact if univariate polynomials of degree < count for n, m in product(range(count), repeat=2): quad = FullTensorQuadrature([count, count], [nodes_and_weights] * 2) result = quad.apply(lambda xs: xs[0]**n * xs[1]**m) if n % 2 == 1 or m % 2 == 1: wanted = 0. else: wanted = 2. / (n + 1) * 2. / (m + 1) self.assertAlmostEqual( result, wanted, places=10, msg="GC quad with count = {}, 2d poly exactness, n={}, m={}". format(count, n, m)) # sparse test 1d for level in range(4): quad = SparseQuadrature(level, nesting, [nodes_and_weights]) count = quad.get_nodes_count() for n in range(count): result = quad.apply(lambda xs: xs[0]**n) wanted = 2. / (n + 1) if n % 2 == 0 else 0. self.assertAlmostEqual( result, wanted, places=10, msg= "GC sparse quad on level={} with count = {}, n={} polynomial exactness" .format(level, count, n)) # sparse test nd from util.quadrature.helpers import multi_index_bounded_sum from util.analysis import mul_prod for level in range(5): for dim in range(1, 4): quad = SparseQuadrature(level, nesting, [nodes_and_weights] * dim) for ind in multi_index_bounded_sum(dim, level + 1): result = quad.apply( lambda xs: mul_prod(x**i for x, i in zip(xs, ind))) if any(i % 2 == 1 for i in ind): wanted = 0. else: wanted = mul_prod(2. / (i + 1) for i in ind) self.assertAlmostEqual( result, wanted, places=10, msg="CG sparse {}d on level={}, ind={}, quadpoints={}". format(dim, level, ind, quad.get_nodes_count()))
def multi_poly(ys): return mul_prod( basis.polys(index)(y) for y, index, basis in zip(ys, multi_index, basis_list))
def beta_expectancy_func_simple(*ys, i, k, x): res3 = mul_prod( distr.weight(y) for y, distr in zip(ys, chaos.get_distributions())) return trial.beta( [x], trial.transform_values(ys)) * basis[i](ys) * basis[k](ys) * res3
def integrate_func_simple(*ys, i): res3 = mul_prod( distr.weight(y) for y, distr in zip(ys, chaos.get_distributions())) return function( project_trial.transform_values(ys)) * basis[i](ys) * res3
def weight(xs): return mul_prod( distr.weight(x) for x, distr in zip(xs, distributions))
def function_transformed(*ys): return (function(ys) * mul_prod( distr.weight(y) for y, distr in zip(ys, self.variable_distributions)))
def alpha_expectancy_func_simple(*ys, i, k): res1 = trial.alpha(trial.transform_values(ys)) res2 = basis[i](ys) * basis[k](ys) res3 = mul_prod( distr.weight(y) for y, distr in zip(ys, chaos.get_distributions())) return res1 * res2 * res3