def add_watches(self, watches): """Add quantities that are printed after every time step.""" from pytools import Record class WatchInfo(Record): pass for watch in watches: if isinstance(watch, tuple): display, expr = watch else: display = watch expr = watch parsed = self._parse_expr(expr) parsed, dep_data = self._get_expr_dep_data(parsed) from pytools import any self.have_nonlocal_watches = self.have_nonlocal_watches or \ any(dd.nonlocal_agg for dd in dep_data) from pymbolic import compile compiled = compile(parsed, [dd.varname for dd in dep_data]) watch_info = WatchInfo(display=display, parsed=parsed, dep_data=dep_data, compiled=compiled) self.watches.append(watch_info)
def method_matrix_func(self): try: return self.method_matrix_func_cache except AttributeError: from pymbolic import var stepper = self.method_fac(var("dt")) mat = self.matrix def f2f_rhs(t, yf, ys): return fold_constants(expand(mat[0, 0] * yf())) def s2f_rhs(t, yf, ys): return fold_constants(expand(mat[0, 1] * ys())) def f2s_rhs(t, yf, ys): return fold_constants(expand(mat[1, 0] * yf())) def s2s_rhs(t, yf, ys): return fold_constants(expand(mat[1, 1] * ys())) method_matrix, _ = make_method_matrix(stepper, rhss=(f2f_rhs, s2f_rhs, f2s_rhs, s2s_rhs), f_size=1, s_size=1) from pymbolic import compile self.method_matrix_func_cache = compile(method_matrix) return self.method_matrix_func_cache
def test_compile(): from pymbolic import parse, compile code = compile(parse("x ** y"), ["x", "y"]) assert code(2, 5) == 32 # Test pickling of compiled code. import pickle code = pickle.loads(pickle.dumps(code)) assert code(3, 3) == 27
def get_expr_dataset(self, expression, description=None, unit=None): """Prepare a time-series dataset for a given expression. @arg expression: A C{pymbolic} expression that may involve the time-series variables and the constants in this :class:`LogManager`. If there is data from multiple ranks for a quantity occuring in this expression, an aggregator may have to be specified. @return: C{(description, unit, table)}, where C{table} is a list of tuples C{(tick_nbr, value)}. Aggregators are specified as follows: - C{qty.min}, C{qty.max}, C{qty.avg}, C{qty.sum}, C{qty.norm2} - C{qty[rank_nbr]} - C{qty.loc} """ parsed = self._parse_expr(expression) parsed, dep_data = self._get_expr_dep_data(parsed) # aggregate table data for dd in dep_data: table = self.get_table(dd.name) table.sort(["step"]) dd.table = table.aggregated(["step"], "value", dd.agg_func).data # evaluate unit and description, if necessary if unit is None: from pymbolic import substitute, parse unit_dict = dict((dd.varname, dd.qdat.unit) for dd in dep_data) from pytools import all if all(v is not None for v in six.itervalues(unit_dict)): unit_dict = dict( (k, parse(v)) for k, v in six.iteritems(unit_dict)) unit = substitute(parsed, unit_dict) else: unit = None if description is None: description = expression # compile and evaluate from pymbolic import compile compiled = compile(parsed, [dd.varname for dd in dep_data]) data = [] for key, values in _join_by_first_of_tuple(dd.table for dd in dep_data): try: data.append((key, compiled(*values))) except ZeroDivisionError: pass return (description, unit, data)
def get_expr_dataset(self, expression, description=None, unit=None): """Prepare a time-series dataset for a given expression. @arg expression: A C{pymbolic} expression that may involve the time-series variables and the constants in this :class:`LogManager`. If there is data from multiple ranks for a quantity occuring in this expression, an aggregator may have to be specified. @return: C{(description, unit, table)}, where C{table} is a list of tuples C{(tick_nbr, value)}. Aggregators are specified as follows: - C{qty.min}, C{qty.max}, C{qty.avg}, C{qty.sum}, C{qty.norm2} - C{qty[rank_nbr]} - C{qty.loc} """ parsed = self._parse_expr(expression) parsed, dep_data = self._get_expr_dep_data(parsed) # aggregate table data for dd in dep_data: table = self.get_table(dd.name) table.sort(["step"]) dd.table = table.aggregated(["step"], "value", dd.agg_func).data # evaluate unit and description, if necessary if unit is None: from pymbolic import substitute, parse unit_dict = dict((dd.varname, dd.qdat.unit) for dd in dep_data) from pytools import all if all(v is not None for v in six.itervalues(unit_dict)): unit_dict = dict((k, parse(v)) for k, v in six.iteritems(unit_dict)) unit = substitute(parsed, unit_dict) else: unit = None if description is None: description = expression # compile and evaluate from pymbolic import compile compiled = compile(parsed, [dd.varname for dd in dep_data]) data = [] for key, values in _join_by_first_of_tuple(dd.table for dd in dep_data): try: data.append((key, compiled(*values))) except ZeroDivisionError: pass return (description, unit, data)
def fast_evaluator(matrix, sparse=False): """ Generate a function to evaluate a step matrix quickly. The input comes from StepMatrixFinder. """ # First, rename variables in the matrix to names that are acceptable Python # identifiers. We make use of dagrt's KeyToUniqueNameMap. from dagrt.codegen.utils import KeyToUniqueNameMap name_map = KeyToUniqueNameMap(forced_prefix="matrix") def make_identifier(symbol): from pymbolic import var assert isinstance(symbol, var) return var(name_map.get_or_make_name_for_key(symbol.name)) def get_var_order_from_name_map(): order = sorted(name_map) return (order, [name_map.get_or_make_name_for_key(key) for key in order]) from pymbolic.mapper.substitutor import SubstitutionMapper substitutor = SubstitutionMapper(make_identifier) from pymbolic import compile # functools.partial ensures the resulting object is picklable. from functools import partial if sparse: data = [substitutor(entry) for entry in matrix.data] var_order, renamed_vars = get_var_order_from_name_map() compiled_entries = [compile(entry, renamed_vars) for entry in data] compiled_matrix = matrix.copy(data=compiled_entries) else: matrix = substitutor(matrix) var_order, renamed_vars = get_var_order_from_name_map() compiled_matrix = compile(matrix, renamed_vars) return partial(_eval_compiled_matrix, compiled_matrix, var_order)
def add_watches(self, watches): """Add quantities that are printed after every time step.""" from pytools import Record for watch in watches: parsed = self._parse_expr(watch) parsed, dep_data = self._get_expr_dep_data(parsed) from pymbolic import compile compiled = compile(parsed, [dd.varname for dd in dep_data]) watch_info = Record(expr=watch, parsed=parsed, dep_data=dep_data, compiled=compiled) self.watches.append(watch_info)
def get_expr_dataset(self, expression, description=None, unit=None): """Prepare a time-series dataset for a given expression. @arg expression: A C{pymbolic} expression that may involve the time-series variables and the constants in this L{LogManager}. If there is data from multiple ranks for a quantity occuring in this expression, an aggregator may have to be specified. @return: C{(description, unit, table)}, where C{table} is a list of tuples C{(tick_nbr, value)}. Aggregators are specified as follows: - C{qty.min}, C{qty.max}, C{qty.avg}, C{qty.sum}, C{qty.norm2} - C{qty[rank_nbr] """ parsed = self._parse_expr(expression) parsed, dep_data = self._get_expr_dep_data(parsed) # aggregate table data for dd in dep_data: table = self.get_table(dd.name) table.sort(["step"]) dd.table = table.aggregated(["step"], "value", dd.agg_func).data # evaluate unit and description, if necessary if unit is None: from pymbolic import substitute, parse unit = substitute(parsed, dict((dd.varname, parse(dd.qdat.unit)) for dd in dep_data) ) if description is None: description = expression # compile and evaluate from pymbolic import compile compiled = compile(parsed, [dd.varname for dd in dep_data]) return (description, unit, [(key, compiled(*values)) for key, values in _join_by_first_of_tuple( dd.table for dd in dep_data) ])
def test(): from hedge.timestep.multirate_ab import \ TwoRateAdamsBashforthMethodBuilder from pymbolic import var stepper = TwoRateAdamsBashforthMethodBuilder( method="Fqsr", large_dt=var("dt"), substep_count=2, order=1) mat = numpy.random.randn(2, 2) def f2f_rhs(t, yf, ys): return fold_constants(expand(mat[0, 0] * yf())) def s2f_rhs(t, yf, ys): return fold_constants(expand(mat[0, 1] * ys())) def f2s_rhs(t, yf, ys): return fold_constants(expand(mat[1, 0] * yf())) def s2s_rhs(t, yf, ys): return fold_constants(expand(mat[1, 1] * ys())) z, vars = make_method_matrix(stepper, rhss=(f2f_rhs, s2f_rhs, f2s_rhs, s2s_rhs), f_size=1, s_size=1) from pymbolic import compile num_mat_func = compile(z) num_mat = num_mat_func(0.1) if False: from pymbolic import substitute num_mat_2 = numpy.array( fold_constants(substitute(z, dt=0.1)), dtype=numpy.complex128) print(la.norm(num_mat - num_mat_2)) if True: for row, ivar in zip(num_mat, vars): print("".join("*" if entry else "." for entry in row), ivar)
def __init__(self, op1_subset=full_subset, op2_subset=full_subset, result_subset=full_subset): """Construct a subset-able cross product. :param op1_subset: The subset of indices of operand 1 to be taken into account. Given as a 3-sequence of bools. :param op2_subset: The subset of indices of operand 2 to be taken into account. Given as a 3-sequence of bools. :param result_subset: The subset of indices of the result that are calculated. Given as a 3-sequence of bools. """ def subset_indices(subset): return [ i for i, use_component in enumerate(subset) if use_component ] self.op1_subset = op1_subset self.op2_subset = op2_subset self.result_subset = result_subset import pymbolic op1 = pymbolic.var("x") op2 = pymbolic.var("y") self.functions = [] self.component_lcjk = [] for i, use_component in enumerate(result_subset): if use_component: this_expr = 0 this_component = [] for j, j_real in enumerate(subset_indices(op1_subset)): for k, k_real in enumerate(subset_indices(op2_subset)): lc = levi_civita((i, j_real, k_real)) if lc != 0: this_expr += lc * op1[j] * op2[k] this_component.append((lc, j, k)) self.functions.append( pymbolic.compile(this_expr, variables=[op1, op2])) self.component_lcjk.append(this_component)
def __init__(self, kernel): # a mapping from parameter names to a list of tuples # (arg_name, axis_nr, function), where function is a # unary function of kernel.arg_dict[arg_name].shape[axis_nr] # returning the desired parameter. self.param_to_sources = param_to_sources = {} param_names = kernel.all_params() from loopy.kernel.data import GlobalArg from loopy.symbolic import DependencyMapper from pymbolic import compile dep_map = DependencyMapper() from pymbolic import var for arg in kernel.args: if isinstance(arg, GlobalArg): for axis_nr, shape_i in enumerate(arg.shape): deps = dep_map(shape_i) if len(deps) == 1: dep, = deps if dep.name in param_names: from pymbolic.algorithm import solve_affine_equations_for try: # friggin' overkill :) param_expr = solve_affine_equations_for([dep.name], [(shape_i, var("shape_i"))])[ dep.name ] except: # went wrong? oh well pass else: param_func = compile(param_expr, ["shape_i"]) param_to_sources.setdefault(dep.name, []).append((arg.name, axis_nr, param_func))
def __init__(self, op1_subset=full_subset, op2_subset=full_subset, result_subset=full_subset): """Construct a subset-able cross product. :param op1_subset: The subset of indices of operand 1 to be taken into account. Given as a 3-sequence of bools. :param op2_subset: The subset of indices of operand 2 to be taken into account. Given as a 3-sequence of bools. :param result_subset: The subset of indices of the result that are calculated. Given as a 3-sequence of bools. """ def subset_indices(subset): return [i for i, use_component in enumerate(subset) if use_component] self.op1_subset = op1_subset self.op2_subset = op2_subset self.result_subset = result_subset import pymbolic op1 = pymbolic.var("x") op2 = pymbolic.var("y") self.functions = [] self.component_lcjk = [] for i, use_component in enumerate(result_subset): if use_component: this_expr = 0 this_component = [] for j, j_real in enumerate(subset_indices(op1_subset)): for k, k_real in enumerate(subset_indices(op2_subset)): lc = levi_civita((i, j_real, k_real)) if lc != 0: this_expr += lc*op1.index(j)*op2.index(k) this_component.append((lc, j, k)) self.functions.append(pymbolic.compile(this_expr, variables=[op1, op2])) self.component_lcjk.append(this_component)
def __init__(self, kernel): # a mapping from parameter names to a list of tuples # (arg_name, axis_nr, function), where function is a # unary function of kernel.arg_dict[arg_name].shape[axis_nr] # returning the desired parameter. self.param_to_sources = param_to_sources = {} param_names = kernel.all_params() from loopy.kernel.data import GlobalArg from loopy.symbolic import DependencyMapper from pymbolic import compile dep_map = DependencyMapper() from pymbolic import var for arg in kernel.args: if isinstance(arg, GlobalArg): for axis_nr, shape_i in enumerate(arg.shape): deps = dep_map(shape_i) if len(deps) == 1: dep, = deps if dep.name in param_names: from pymbolic.algorithm import solve_affine_equations_for try: # friggin' overkill :) param_expr = solve_affine_equations_for( [dep.name], [(shape_i, var("shape_i"))])[dep.name] except: # went wrong? oh well pass else: param_func = compile(param_expr, ["shape_i"]) param_to_sources.setdefault( dep.name, []).append( (arg.name, axis_nr, param_func))
def test_elliptic(): """Test various properties of elliptic operators.""" from hedge.tools import unit_vector def matrix_rep(op): h, w = op.shape mat = numpy.zeros(op.shape) for j in range(w): mat[:, j] = op(unit_vector(w, j)) return mat def check_grad_mat(): import pyublas if not pyublas.has_sparse_wrappers(): return grad_mat = op.grad_matrix() # print len(discr), grad_mat.nnz, type(grad_mat) for i in range(10): u = numpy.random.randn(len(discr)) mat_result = grad_mat * u op_result = numpy.hstack(op.grad(u)) err = la.norm(mat_result - op_result) * la.norm(op_result) assert la.norm(mat_result - op_result) * la.norm(op_result) < 1e-5 def check_matrix_tgt(): big = num.zeros((20, 20), flavor=num.SparseBuildMatrix) small = num.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print small from hedge._internal import MatrixTarget tgt = MatrixTarget(big, 4, 4) tgt.begin(small.shape[0], small.shape[1]) print "YO" tgt.add_coefficients(4, 4, small) print "DUDE" tgt.finalize() print big import pymbolic v_x = pymbolic.var("x") truesol = pymbolic.parse("math.sin(x[0]**2*x[1]**2)") truesol_c = pymbolic.compile(truesol, variables=["x"]) rhs = pymbolic.simplify(pymbolic.laplace(truesol, [v_x[0], v_x[1]])) rhs_c = pymbolic.compile(rhs, variables=["x", "el"]) from hedge.mesh import TAG_ALL, TAG_NONE from hedge.mesh.generator import make_disk_mesh mesh = make_disk_mesh(r=0.5, max_area=0.1, faces=20) mesh = mesh.reordered_by("cuthill") from hedge.backends import CPURunContext rcon = CPURunContext() from hedge.tools import EOCRecorder eocrec = EOCRecorder() for order in [1, 2, 3, 4, 5]: for flux in ["ldg", "ip"]: from hedge.discretization.local import TriangleDiscretization discr = rcon.make_discretization( mesh, TriangleDiscretization(order), debug=discr_class.noninteractive_debug_flags() ) from hedge.data import GivenFunction from hedge.models.poisson import PoissonOperator op = PoissonOperator( discr.dimensions, dirichlet_tag=TAG_ALL, dirichlet_bc=GivenFunction(lambda x, el: truesol_c(x)), neumann_tag=TAG_NONE, ) bound_op = op.bind(discr) if order <= 3: mat = matrix_rep(bound_op) sym_err = la.norm(mat - mat.T) # print sym_err assert sym_err < 1e-12 # check_grad_mat() from hedge.iterative import parallel_cg truesol_v = discr.interpolate_volume_function(lambda x, el: truesol_c(x)) sol_v = -parallel_cg( rcon, -bound_op, bound_op.prepare_rhs(discr.interpolate_volume_function(rhs_c)), tol=1e-10, max_iterations=40000, ) eocrec.add_data_point(order, discr.norm(sol_v - truesol_v)) # print eocrec.pretty_print() assert eocrec.estimate_order_of_convergence()[0, 1] > 8
def newton_interpolation_function(x, y): return pymbolic.compile(newton_interpolation_polynomial(x, y), ["x"])
def test_elliptic(): """Test various properties of elliptic operators.""" from hedge.tools import unit_vector def matrix_rep(op): h, w = op.shape mat = numpy.zeros(op.shape) for j in range(w): mat[:, j] = op(unit_vector(w, j)) return mat def check_grad_mat(): import pyublas if not pyublas.has_sparse_wrappers(): return grad_mat = op.grad_matrix() #print len(discr), grad_mat.nnz, type(grad_mat) for i in range(10): u = numpy.random.randn(len(discr)) mat_result = grad_mat * u op_result = numpy.hstack(op.grad(u)) err = la.norm(mat_result - op_result) * la.norm(op_result) assert err < 1e-5 def check_matrix_tgt(): big = numpy.zeros((20, 20), flavor=numpy.SparseBuildMatrix) small = numpy.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print small from hedge._internal import MatrixTarget tgt = MatrixTarget(big, 4, 4) tgt.begin(small.shape[0], small.shape[1]) print "YO" tgt.add_coefficients(4, 4, small) print "DUDE" tgt.finalize() print big import pymbolic v_x = pymbolic.var("x") truesol = pymbolic.parse("math.sin(x[0]**2*x[1]**2)") truesol_c = pymbolic.compile(truesol, variables=["x"]) def laplace(expression, variables): return sum( pymbolic.diff(pymbolic.diff(expression, var), var) for var in variables) rhs = laplace(truesol, [v_x[0], v_x[1]]) rhs_c = pymbolic.compile(rhs, variables=["x", "el"]) from hedge.mesh import TAG_ALL, TAG_NONE from hedge.mesh.generator import make_disk_mesh mesh = make_disk_mesh(r=0.5, max_area=0.1, faces=20) mesh = mesh.reordered_by("cuthill") from hedge.backends import CPURunContext rcon = CPURunContext() from hedge.tools import EOCRecorder eocrec = EOCRecorder() for order in [1, 2, 3, 4, 5]: for flux in ["ldg", "ip"]: from hedge.discretization.local import TriangleDiscretization discr = rcon.make_discretization( mesh, TriangleDiscretization(order), debug=discr_class.noninteractive_debug_flags()) from hedge.data import GivenFunction from hedge.models.poisson import PoissonOperator op = PoissonOperator( discr.dimensions, dirichlet_tag=TAG_ALL, dirichlet_bc=GivenFunction(lambda x, el: truesol_c(x)), neumann_tag=TAG_NONE) bound_op = op.bind(discr) if order <= 3: mat = matrix_rep(bound_op) sym_err = la.norm(mat - mat.T) #print sym_err assert sym_err < 1e-12 #check_grad_mat() from hedge.iterative import parallel_cg truesol_v = discr.interpolate_volume_function( lambda x, el: truesol_c(x)) sol_v = -parallel_cg(rcon, -bound_op, bound_op.prepare_rhs( discr.interpolate_volume_function(rhs_c)), tol=1e-10, max_iterations=40000) eocrec.add_data_point(order, discr.norm(sol_v - truesol_v)) #print eocrec.pretty_print() assert eocrec.estimate_order_of_convergence()[0, 1] > 8