def __call__(self, queue, targets, sources, index_set, **kwargs): """Construct a set of blocks of the full P2P interaction matrix. The blocks are returned as one-dimensional arrays, for performance and storage reasons. If the two-dimensional form is desired, it can be obtained using the information in the `index_set` for a block :math:`i` in the following way: .. code-block:: python blkranges = index_set.linear_ranges() blkshape = index_set.block_shape(i) block2d = result[blkranges[i]:blkranges[i + 1]].reshape(*blkshape) :arg targets: target point coordinates. :arg sources: source point coordinates. :arg index_set: a :class:`sumpy.tools.MatrixBlockIndexRanges` used to define the blocks. :return: a tuple of one-dimensional arrays of kernel evaluations at target-source pairs described by `index_set`. """ from pytools.obj_array import is_obj_array knl = self.get_cached_optimized_kernel( targets_is_obj_array=( is_obj_array(targets) or isinstance(targets, (tuple, list))), sources_is_obj_array=( is_obj_array(sources) or isinstance(sources, (tuple, list)))) return knl(queue, targets=targets, sources=sources, tgtindices=index_set.linear_row_indices, srcindices=index_set.linear_col_indices, **kwargs)
def __call__(self, queue, targets, sources, index_set, **kwargs): """Construct a set of blocks of the full P2P interaction matrix. The blocks are returned as one-dimensional arrays, for performance and storage reasons. If the two-dimensional form is desired, it can be obtained using the information in the `index_set` for a block :math:`i` in the following way: .. code-block:: python blkranges = index_set.linear_ranges() blkshape = index_set.block_shape(i) block2d = result[blkranges[i]:blkranges[i + 1]].reshape(*blkshape) :arg targets: target point coordinates. :arg sources: source point coordinates. :arg index_set: a :class:`sumpy.tools.MatrixBlockIndexRanges` used to define the blocks. :return: a tuple of one-dimensional arrays of kernel evaluations at target-source pairs described by `index_set`. """ from pytools.obj_array import is_obj_array knl = self.get_cached_optimized_kernel( targets_is_obj_array=(is_obj_array(targets) or isinstance(targets, (tuple, list))), sources_is_obj_array=(is_obj_array(sources) or isinstance(sources, (tuple, list)))) return knl(queue, targets=targets, sources=sources, tgtindices=index_set.linear_row_indices, srcindices=index_set.linear_col_indices, **kwargs)
def field_equal(a, b): a_is_oa = is_obj_array(a) assert a_is_oa == is_obj_array(b) if a_is_oa: return (a == b).all() else: return a == b
def obj_array_equal(a, b): a_is_oa = is_obj_array(a) assert a_is_oa == is_obj_array(b) if a_is_oa: return np.array_equal(a, b) else: return a == b
def __call__(self, queue, targets, sources, **kwargs): from pytools.obj_array import is_obj_array knl = self.get_cached_optimized_kernel( targets_is_obj_array=(is_obj_array(targets) or isinstance(targets, (tuple, list))), sources_is_obj_array=(is_obj_array(sources) or isinstance(sources, (tuple, list)))) return knl(queue, sources=sources, targets=targets, **kwargs)
def __call__(self, queue, targets, sources, **kwargs): from pytools.obj_array import is_obj_array knl = self.get_cached_optimized_kernel( targets_is_obj_array=( is_obj_array(targets) or isinstance(targets, (tuple, list))), sources_is_obj_array=( is_obj_array(sources) or isinstance(sources, (tuple, list)))) return knl(queue, sources=sources, targets=targets, **kwargs)
def separate_by_real_and_imag(data, real_only): for name, field in data: from pytools.obj_array import log_shape, is_obj_array ls = log_shape(field) if is_obj_array(field): assert len(ls) == 1 from pytools.obj_array import ( oarray_real_copy, oarray_imag_copy, with_object_array_or_scalar) if field[0].dtype.kind == "c": if real_only: yield (name, with_object_array_or_scalar(oarray_real_copy, field)) else: yield (name+"_r", with_object_array_or_scalar(oarray_real_copy, field)) yield (name+"_i", with_object_array_or_scalar(oarray_imag_copy, field)) else: yield (name, field) else: # ls == () if field.dtype.kind == "c": yield (name+"_r", field.real.copy()) yield (name+"_i", field.imag.copy()) else: yield (name, field)
def with_object_array_or_scalar_n_args(f, *args): oarray_arg_indices = [] for i, arg in enumerate(args): if is_obj_array(arg): oarray_arg_indices.append(i) if not oarray_arg_indices: return f(*args) leading_oa_index = oarray_arg_indices[0] ls = log_shape(args[leading_oa_index]) if ls != (): from pytools import indices_in_shape result = np.zeros(ls, dtype=object) new_args = list(args) for i in indices_in_shape(ls): for arg_i in oarray_arg_indices: new_args[arg_i] = args[arg_i][i] result[i] = f(*new_args) return result else: return f(*args)
def inverse_mass(self, vec): if is_obj_array(vec): return with_object_array_or_scalar( lambda el: self.inverse_mass(el), vec) @memoize_in(self, "elwise_linear_knl") def knl(): knl = lp.make_kernel("""{[k,i,j]: 0<=k<nelements and 0<=i<ndiscr_nodes_out and 0<=j<ndiscr_nodes_in}""", "result[k,i] = sum(j, mat[i, j] * vec[k, j])", default_offset=lp.auto, name="diff") knl = lp.split_iname(knl, "i", 16, inner_tag="l.0") return lp.tag_inames(knl, dict(k="g.0")) discr = self.volume_discr result = discr.empty(queue=vec.queue, dtype=vec.dtype) for grp in discr.groups: matrix = self.get_inverse_mass_matrix(grp, vec.dtype) knl()(vec.queue, mat=matrix, result=grp.view(result), vec=grp.view(vec)) return result / self.vol_jacobian()
def separate_by_real_and_imag(data, real_only): for name, field in data: from pytools.obj_array import log_shape, is_obj_array ls = log_shape(field) if is_obj_array(field): assert len(ls) == 1 from pytools.obj_array import (oarray_real_copy, oarray_imag_copy, with_object_array_or_scalar) if field[0].dtype.kind == "c": if real_only: yield (name, with_object_array_or_scalar(oarray_real_copy, field)) else: yield (name + "_r", with_object_array_or_scalar(oarray_real_copy, field)) yield (name + "_i", with_object_array_or_scalar(oarray_imag_copy, field)) else: yield (name, field) else: # ls == () if field.dtype.kind == "c": yield (name + "_r", field.real.copy()) yield (name + "_i", field.imag.copy()) else: yield (name, field)
def scipy_op(self, queue, arg_name, domains=None, **extra_args): """ :arg domains: a list of discretization identifiers or *None* values indicating the domains on which each component of the solution vector lives. *None* values indicate that the component is a scalar. If *None*, :class:`pytential.symbolic.primitives.DEFAULT_TARGET`, is required to be a key in :attr:`places`. """ from pytools.obj_array import is_obj_array if domains is None: from pytential.symbolic.primitives import DEFAULT_TARGET if DEFAULT_TARGET not in self.places: raise RuntimeError("'domains is None' requires " "DEFAULT_TARGET to be defined") dom_name = DEFAULT_TARGET if is_obj_array(self.code.result): domains = len(self.code.result)*[dom_name] else: domains = [dom_name] elif not isinstance(domains, list): dom_name = domains if is_obj_array(self.code.result): domains = len(self.code.result)*[dom_name] else: domains = [dom_name] total_dofs = 0 starts_and_ends = [] for dom_name in domains: if dom_name is None: size = 1 else: size = self.places[dom_name].nnodes starts_and_ends.append((total_dofs, total_dofs+size)) total_dofs += size # Hidden assumption: Number of input components # equals number of output compoments. But IMO that's # fair, since these operators are usually only used # for linear system solving, in which case the assumption # has to be true. return MatVecOp(self, queue, arg_name, total_dofs, starts_and_ends, extra_args)
def reorder_potentials(self, potentials): from pytools.obj_array import is_obj_array, with_object_array_or_scalar assert is_obj_array(potentials) def reorder(x): return x.with_queue(self.queue)[self.tree.sorted_target_ids] return with_object_array_or_scalar(reorder, potentials)
def get_array_module(vec): try: from pyopencl.tools import array_module from pytools.obj_array import is_obj_array if is_obj_array(vec): return array_module(vec[0]) else: return array_module(vec) except ImportError: return np
def vec_times(self, vec, operand): from pytools.obj_array import is_obj_array if is_obj_array(operand): if len(operand) != self.dimensions: raise ValueError("operand of vec_times must have %d dimensions" % self.dimensions) return np.dot(vec, operand) else: return vec*operand
def apply_diff(self, nabla, operand): from pytools.obj_array import make_obj_array, is_obj_array if is_obj_array(operand): if len(operand) != self.dimensions: raise ValueError("operand of apply_diff must have %d dimensions" % self.dimensions) return sum(nabla[i](operand[i]) for i in range(self.dimensions)) else: return make_obj_array( [nabla[i](operand) for i in range(self.dimensions)])
def dot_dataflow_graph(code, max_node_label_length=30, label_wrap_width=50): origins = {} node_names = {} result = [ "initial [label=\"initial\"]" "result [label=\"result\"]"] for num, insn in enumerate(code.instructions): node_name = "node%d" % num node_names[insn] = node_name node_label = str(insn) if max_node_label_length is not None: node_label = node_label[:max_node_label_length] if label_wrap_width is not None: from pytools import word_wrap node_label = word_wrap(node_label, label_wrap_width, wrap_using="\n ") node_label = node_label.replace("\n", "\\l") + "\\l" result.append("%s [ label=\"p%d: %s\" shape=box ];" % ( node_name, insn.priority, node_label)) for assignee in insn.get_assignees(): origins[assignee] = node_name def get_orig_node(expr): from pymbolic.primitives import Variable if isinstance(expr, Variable): return origins.get(expr.name, "initial") else: return "initial" def gen_expr_arrow(expr, target_node): result.append("%s -> %s [label=\"%s\"];" % (get_orig_node(expr), target_node, expr)) for insn in code.instructions: for dep in insn.get_dependencies(): gen_expr_arrow(dep, node_names[insn]) from pytools.obj_array import is_obj_array if is_obj_array(code.result): for subexp in code.result: gen_expr_arrow(subexp, "result") else: gen_expr_arrow(code.result, "result") return "digraph dataflow {\n%s\n}\n" % "\n".join(result)
def dot_dataflow_graph(code, max_node_label_length=30, label_wrap_width=50): origins = {} node_names = {} result = ["initial [label=\"initial\"]" "result [label=\"result\"]"] for num, insn in enumerate(code.instructions): node_name = "node%d" % num node_names[insn] = node_name node_label = str(insn) if (max_node_label_length is not None and not isinstance(insn, LoopyKernelInstruction)): node_label = node_label[:max_node_label_length] if label_wrap_width is not None: from pytools import word_wrap node_label = word_wrap(node_label, label_wrap_width, wrap_using="\n ") node_label = node_label.replace("\n", "\\l") + "\\l" result.append("%s [ label=\"p%d: %s\" shape=box ];" % (node_name, insn.priority, node_label)) for assignee in insn.get_assignees(): origins[assignee] = node_name def get_orig_node(expr): from pymbolic.primitives import Variable if isinstance(expr, Variable): return origins.get(expr.name, "initial") else: return "initial" def gen_expr_arrow(expr, target_node): result.append("%s -> %s [label=\"%s\"];" % (get_orig_node(expr), target_node, expr)) for insn in code.instructions: for dep in insn.get_dependencies(): gen_expr_arrow(dep, node_names[insn]) from pytools.obj_array import is_obj_array if is_obj_array(code.result): for subexp in code.result: gen_expr_arrow(subexp, "result") else: gen_expr_arrow(code.result, "result") return "digraph dataflow {\n%s\n}\n" % "\n".join(result)
def vec_times(self, vec, operand): from pytools.obj_array import is_obj_array if is_obj_array(operand): if len(operand) != self.dimensions: raise ValueError( "operand of vec_times must have %d dimensions" % self.dimensions) return np.dot(vec, operand) else: return vec * operand
def apply_diff(self, nabla, operand): from pytools.obj_array import make_obj_array, is_obj_array if is_obj_array(operand): if len(operand) != self.dimensions: raise ValueError( "operand of apply_diff must have %d dimensions" % self.dimensions) return sum(nabla[i](operand[i]) for i in range(self.dimensions)) else: return make_obj_array( [nabla[i](operand) for i in range(self.dimensions)])
def __call__(self, discr, t, fields, x, make_empty): result = self.make_func(discr)(t=numpy.float64(t), x=x, fields=fields) # make sure we return no scalars in the result from pytools.obj_array import log_shape, is_obj_array if is_obj_array(result): from pytools import indices_in_shape from hedge.optemplate.tools import is_scalar for i in indices_in_shape(log_shape(result)): if is_scalar(result[i]): result[i] = make_empty().fill(result[i]) return result
def __call__(self, operand, *args, **kwargs): # If the call is handed an object array full of operands, # return an object array of the operator applied to each of the # operands. from pytools.obj_array import is_obj_array, with_object_array_or_scalar if is_obj_array(operand): def make_op(operand_i): return self(operand_i, *args, **kwargs) return with_object_array_or_scalar(make_op, operand) else: return var.__call__(self, operand, *args, **kwargs)
def __call__(self, discr, t, fields, x, make_empty): result = self.make_func(discr)( t=numpy.float64(t), x=x, fields=fields) # make sure we return no scalars in the result from pytools.obj_array import log_shape, is_obj_array if is_obj_array(result): from pytools import indices_in_shape from hedge.optemplate.tools import is_scalar for i in indices_in_shape(log_shape(result)): if is_scalar(result[i]): result[i] = make_empty().fill(result[i]) return result
def with_object_array_or_scalar(f, field, obj_array_only=False): if obj_array_only: if is_obj_array(field): ls = field.shape else: ls = () else: ls = log_shape(field) if ls != (): from pytools import indices_in_shape result = np.zeros(ls, dtype=object) for i in indices_in_shape(ls): result[i] = f(field[i]) return result else: return f(field)
def __init__(self, structured_vec): from pytools.obj_array import is_obj_array self.is_structured = is_obj_array(structured_vec) self.array_module = get_array_module(structured_vec) if self.is_structured: self.slices = [] num_dofs = 0 for entry in structured_vec: if isinstance(entry, self.array_module.ndarray): length = len(entry) else: length = 1 self.slices.append(slice(num_dofs, num_dofs+length)) num_dofs += length
def face_mass(self, vec): if is_obj_array(vec): return with_object_array_or_scalar(lambda el: self.face_mass(el), vec) @memoize_in(self, "face_mass_knl") def knl(): knl = lp.make_kernel( """{[k,i,f,j]: 0<=k<nelements and 0<=f<nfaces and 0<=i<nvol_nodes and 0<=j<nface_nodes}""", "result[k,i] = sum(f, sum(j, mat[i, f, j] * vec[f, k, j]))", default_offset=lp.auto, name="face_mass") knl = lp.split_iname(knl, "i", 16, inner_tag="l.0") return lp.tag_inames(knl, dict(k="g.0")) all_faces_conn = self.get_connection("vol", "all_faces") all_faces_discr = all_faces_conn.to_discr vol_discr = all_faces_conn.from_discr result = vol_discr.empty(queue=vec.queue, dtype=vec.dtype) fj = self.face_jacobian("all_faces") vec = vec * fj assert len(all_faces_discr.groups) == len(vol_discr.groups) for afgrp, volgrp in zip(all_faces_discr.groups, vol_discr.groups): nfaces = volgrp.mesh_el_group.nfaces matrix = self.get_local_face_mass_matrix(afgrp, volgrp, vec.dtype) input_view = afgrp.view(vec).reshape(nfaces, volgrp.nelements, afgrp.nunit_nodes) knl()(vec.queue, mat=matrix, result=volgrp.view(result), vec=input_view) return result
def dd_axis(axis, ambient_dim, operand): """Return the derivative along (XYZ) axis *axis* (in *ambient_dim*-dimensional space) of *operand*. """ from pytools.obj_array import is_obj_array, with_object_array_or_scalar if is_obj_array(operand): def dd_axis_comp(operand_i): return dd_axis(axis, ambient_dim, operand_i) return with_object_array_or_scalar(dd_axis_comp, operand) d = Derivative() unit_vector = np.zeros(ambient_dim) unit_vector[axis] = 1 unit_mvector = MultiVector(unit_vector) return d.resolve( (unit_mvector.scalar_product(d.dnabla(ambient_dim))) * d(operand))
def scipy_op(self, queue, arg_name, dtype, domains=None, **extra_args): """ :arg domains: a list of discretization identifiers or *None* values indicating the domains on which each component of the solution vector lives. *None* values indicate that the component is a scalar. If *domains* is *None*, :class:`~pytential.symbolic.primitives.DEFAULT_TARGET` is required to be a key in :attr:`places`. :returns: An object that (mostly) satisfies the :mod:`scipy.linalg.LinearOperator` protocol, except for accepting and returning :clas:`pyopencl.array.Array` arrays. """ from pytools.obj_array import is_obj_array if is_obj_array(self.code.result): nresults = len(self.code.result) else: nresults = 1 domains = _prepare_domains(nresults, self.places, domains, self.places.auto_target) total_dofs = 0 starts_and_ends = [] for dom_name in domains: if dom_name is None: size = 1 else: size = self.places.get_geometry(dom_name).nnodes starts_and_ends.append((total_dofs, total_dofs + size)) total_dofs += size # Hidden assumption: Number of input components # equals number of output compoments. But IMO that's # fair, since these operators are usually only used # for linear system solving, in which case the assumption # has to be true. return MatVecOp(self, queue, arg_name, dtype, total_dofs, starts_and_ends, extra_args)
def test_matrix_build(ctx_factory, k, curve_f, lpot_id, visualize=False): cl_ctx = ctx_factory() queue = cl.CommandQueue(cl_ctx) # prevent cache 'splosion from sympy.core.cache import clear_cache clear_cache() target_order = 7 qbx_order = 4 nelements = 30 mesh = make_curve_mesh(curve_f, np.linspace(0, 1, nelements + 1), target_order) from meshmode.discretization import Discretization from meshmode.discretization.poly_element import \ InterpolatoryQuadratureSimplexGroupFactory pre_density_discr = Discretization( cl_ctx, mesh, InterpolatoryQuadratureSimplexGroupFactory(target_order)) from pytential.qbx import QBXLayerPotentialSource qbx, _ = QBXLayerPotentialSource(pre_density_discr, 4 * target_order, qbx_order, # Don't use FMM for now fmm_order=False).with_refinement() density_discr = qbx.density_discr op, u_sym, knl_kwargs = _build_op(lpot_id, k=k) bound_op = bind(qbx, op) from pytential.symbolic.execution import build_matrix mat = build_matrix(queue, qbx, op, u_sym).get() if visualize: from sumpy.tools import build_matrix as build_matrix_via_matvec mat2 = bound_op.scipy_op(queue, "u", dtype=mat.dtype, **knl_kwargs) mat2 = build_matrix_via_matvec(mat2) print(la.norm((mat - mat2).real, "fro") / la.norm(mat2.real, "fro"), la.norm((mat - mat2).imag, "fro") / la.norm(mat2.imag, "fro")) import matplotlib.pyplot as pt pt.subplot(121) pt.imshow(np.log10(np.abs(1.0e-20 + (mat - mat2).real))) pt.colorbar() pt.subplot(122) pt.imshow(np.log10(np.abs(1.0e-20 + (mat - mat2).imag))) pt.colorbar() pt.show() if visualize: import matplotlib.pyplot as pt pt.subplot(121) pt.imshow(mat.real) pt.colorbar() pt.subplot(122) pt.imshow(mat.imag) pt.colorbar() pt.show() from sumpy.tools import vector_to_device, vector_from_device np.random.seed(12) for i in range(5): if is_obj_array(u_sym): u = make_obj_array([ np.random.randn(density_discr.nnodes) for _ in range(len(u_sym)) ]) else: u = np.random.randn(density_discr.nnodes) u_dev = vector_to_device(queue, u) res_matvec = np.hstack( list(vector_from_device( queue, bound_op(queue, u=u_dev)))) res_mat = mat.dot(np.hstack(list(u))) abs_err = la.norm(res_mat - res_matvec, np.inf) rel_err = abs_err / la.norm(res_matvec, np.inf) print("AbsErr {:.5e} RelErr {:.5e}".format(abs_err, rel_err)) assert rel_err < 1e-13
def is_field_equal(a, b): if is_obj_array(a): return is_obj_array(b) and (a.shape == b.shape) and (a == b).all() else: return not is_obj_array(b) and a == b
def aggregate_assignments(inf_mapper, instructions, result, max_vectors_in_batch_expr): from pymbolic.primitives import Variable function_registry = inf_mapper.function_registry # {{{ aggregation helpers def get_complete_origins_set(insn, skip_levels=0): try: return insn_to_origins_cache[insn] except KeyError: pass if skip_levels < 0: skip_levels = 0 result = set() for dep in insn.get_dependencies(): if isinstance(dep, Variable): dep_origin = origins_map.get(dep.name, None) if dep_origin is not None: if skip_levels <= 0: result.add(dep_origin) result |= get_complete_origins_set(dep_origin, skip_levels - 1) insn_to_origins_cache[insn] = result return result var_assignees_cache = {} def get_var_assignees(insn): try: return var_assignees_cache[insn] except KeyError: result = set( Variable(assignee) for assignee in insn.get_assignees()) var_assignees_cache[insn] = result return result def aggregate_two_assignments(ass_1, ass_2): names = ass_1.names + ass_2.names from pymbolic.primitives import Variable deps = (ass_1.get_dependencies() | ass_2.get_dependencies()) \ - set(Variable(name) for name in names) return Assign(names=names, exprs=ass_1.exprs + ass_2.exprs, _dependencies=deps, priority=max(ass_1.priority, ass_2.priority)) # }}} # {{{ main aggregation pass insn_to_origins_cache = {} origins_map = dict((assignee, insn) for insn in instructions for assignee in insn.get_assignees()) from pytools import partition from grudge.symbolic.primitives import DTAG_SCALAR unprocessed_assigns, other_insns = partition( lambda insn: (isinstance(insn, Assign) and not isinstance( insn, ToDiscretizationScopedAssign) and not isinstance( insn, FromDiscretizationScopedAssign) and not is_external_call( insn.exprs[0], function_registry) and not any( inf_mapper.infer_for_name(n).domain_tag == DTAG_SCALAR for n in insn.names)), instructions) # filter out zero-flop-count assigns--no need to bother with those processed_assigns, unprocessed_assigns = partition( lambda ass: ass.flop_count() == 0, unprocessed_assigns) # filter out zero assignments from grudge.tools import is_zero i = 0 while i < len(unprocessed_assigns): my_assign = unprocessed_assigns[i] if any(is_zero(expr) for expr in my_assign.exprs): processed_assigns.append(unprocessed_assigns.pop(i)) else: i += 1 # greedy aggregation while unprocessed_assigns: my_assign = unprocessed_assigns.pop() my_deps = my_assign.get_dependencies() my_assignees = get_var_assignees(my_assign) agg_candidates = [] for i, other_assign in enumerate(unprocessed_assigns): other_deps = other_assign.get_dependencies() other_assignees = get_var_assignees(other_assign) if ((my_deps & other_deps or my_deps & other_assignees or other_deps & my_assignees) and my_assign.priority == other_assign.priority): agg_candidates.append((i, other_assign)) did_work = False if agg_candidates: my_indirect_origins = get_complete_origins_set(my_assign, skip_levels=1) for other_assign_index, other_assign in agg_candidates: if max_vectors_in_batch_expr is not None: new_assignee_count = len( set(my_assign.get_assignees()) | set(other_assign.get_assignees())) new_dep_count = len( my_assign.get_dependencies(each_vector=True) | other_assign.get_dependencies(each_vector=True)) if (new_assignee_count + new_dep_count > max_vectors_in_batch_expr): continue other_indirect_origins = get_complete_origins_set( other_assign, skip_levels=1) if (my_assign not in other_indirect_origins and other_assign not in my_indirect_origins): did_work = True # aggregate the two assignments new_assignment = aggregate_two_assignments( my_assign, other_assign) del unprocessed_assigns[other_assign_index] unprocessed_assigns.append(new_assignment) for assignee in new_assignment.get_assignees(): origins_map[assignee] = new_assignment break if not did_work: processed_assigns.append(my_assign) externally_used_names = set(expr for insn in processed_assigns + other_insns for expr in insn.get_dependencies()) from pytools.obj_array import is_obj_array if is_obj_array(result): externally_used_names |= set(expr for expr in result) else: externally_used_names |= set([result]) def schedule_and_finalize_assignment(ass): dep_mapper = _make_dep_mapper(include_subscripts=False) names_exprs = list(zip(ass.names, ass.exprs)) my_assignees = set(name for name, expr in names_exprs) names_exprs_deps = [ (name, expr, set(dep.name for dep in dep_mapper(expr) if isinstance(dep, Variable)) & my_assignees) for name, expr in names_exprs ] ordered_names_exprs = [] available_names = set() while names_exprs_deps: schedulable = [] i = 0 while i < len(names_exprs_deps): name, expr, deps = names_exprs_deps[i] unsatisfied_deps = deps - available_names if not unsatisfied_deps: schedulable.append((str(expr), name, expr)) del names_exprs_deps[i] else: i += 1 # make sure these come out in a constant order schedulable.sort() if schedulable: for key, name, expr in schedulable: ordered_names_exprs.append((name, expr)) available_names.add(name) else: raise RuntimeError("aggregation resulted in an " "impossible assignment") return Assign(names=[name for name, expr in ordered_names_exprs], exprs=[expr for name, expr in ordered_names_exprs], do_not_return=[ Variable(name) not in externally_used_names for name, expr in ordered_names_exprs ], priority=ass.priority) return [ schedule_and_finalize_assignment(ass) for ass in processed_assigns ] + other_insns
def apply_imag(self, args): from pytools.obj_array import is_obj_array arg, = args result = self.rec(arg) assert not is_obj_array(result) # numpy bug with obj_array.imag return result.imag
def hashable_field(f): if is_obj_array(f): return tuple(f) else: return f
def obj_array_to_hashable(f): if is_obj_array(f): return tuple(f) else: return f
def setify_field(f): from hedge.tools import is_obj_array if is_obj_array(f): return set(f) else: return set([f])
def gen_len(expr): from pytools.obj_array import is_obj_array if is_obj_array(expr): return len(expr) else: return 1
def gmres(op, rhs, restart=None, tol=None, x0=None, inner_product=None, maxiter=None, hard_failure=None, no_progress_factor=None, stall_iterations=None, callback=None, progress=False): """Solve a linear system Ax=b by means of GMRES with restarts. :arg op: a callable to evaluate A(x) :arg b: the right hand side :arg restart: the maximum number of iteration after which GMRES algorithm needs to be restarted :arg tol: the required decrease in residual norm :arg inner_product: Must have an interface compatible with :func:`numpy.vdot`. Must return a host scalar. :arg maxiter: the maximum number of iteration permitted :arg hard_failure: If True, raise :exc:`GMRESError` in case of failure. :arg stall_iterations: Number of iterations with residual decrease below *no_progress_factor* indicates stall. Set to 0 to disable stall detection. :return: a :class:`GMRESResult` """ try: from pyopencl.tools import array_module amod = array_module(rhs) except ImportError: amod = np from pytools.obj_array import is_obj_array, make_obj_array if is_obj_array(rhs): stacked_rhs = amod.hstack(rhs) slices = [] num_dofs = 0 for entry in rhs: if isinstance(entry, amod.ndarray): length = len(entry) else: length = 1 slices.append(slice(num_dofs, num_dofs + length)) num_dofs += length else: stacked_rhs = rhs if inner_product is None: inner_product = amod.vdot if callback is None: if progress: callback = ResidualPrinter(inner_product) else: callback = None result = _gmres(op, stacked_rhs, restart=restart, tol=tol, x0=x0, dot=inner_product, maxiter=maxiter, hard_failure=hard_failure, no_progress_factor=no_progress_factor, stall_iterations=stall_iterations, callback=callback) if is_obj_array(rhs): return result.copy( solution=make_obj_array([result.solution[slc] for slc in slices])) else: return result
def interp(self, src, tgt, vec): if is_obj_array(vec): return with_object_array_or_scalar( lambda el: self.interp(src, tgt, el), vec) return self.get_connection(src, tgt)(vec.queue, vec)
def is_equal(a, b): if is_obj_array(a): return is_obj_array(b) and (a.shape == b.shape) and (a == b).all() else: return not is_obj_array(b) and a == b
def build_matrix(queue, places, exprs, input_exprs, domains=None, auto_where=None, context=None): """ :arg queue: a :class:`pyopencl.CommandQueue`. :arg places: a :class:`pytential.symbolic.execution.GeometryCollection`. Alternatively, any list or mapping that is a valid argument for its constructor can also be used. :arg exprs: an array of expressions corresponding to the output block rows of the matrix. May also be a single expression. :arg input_exprs: an array of expressions corresponding to the input block columns of the matrix. May also be a single expression. :arg domains: a list of discretization identifiers (see 'places') or *None* values indicating the domains on which each component of the solution vector lives. *None* values indicate that the component is a scalar. If *None*, *auto_where* or, if it is not provided, :class:`~pytential.symbolic.primitives.DEFAULT_SOURCE` is required to be a key in :attr:`places`. :arg auto_where: For simple source-to-self or source-to-target evaluations, find 'where' attributes automatically. """ if context is None: context = {} from pytools.obj_array import is_obj_array, make_obj_array if not isinstance(places, GeometryCollection): places = GeometryCollection(places, auto_where=auto_where) exprs = _prepare_expr(places, exprs) if not is_obj_array(exprs): exprs = make_obj_array([exprs]) try: input_exprs = list(input_exprs) except TypeError: # not iterable, wrap in a list input_exprs = [input_exprs] domains = _prepare_domains(len(input_exprs), places, domains, places.auto_source) from pytential.symbolic.matrix import MatrixBuilder, is_zero nblock_rows = len(exprs) nblock_columns = len(input_exprs) blocks = np.zeros((nblock_rows, nblock_columns), dtype=np.object) dtypes = [] for ibcol in range(nblock_columns): mbuilder = MatrixBuilder( queue, dep_expr=input_exprs[ibcol], other_dep_exprs=(input_exprs[:ibcol] + input_exprs[ibcol + 1:]), dep_source=places.get_geometry(domains[ibcol]), dep_discr=places.get_discretization(domains[ibcol]), places=places, context=context) for ibrow in range(nblock_rows): block = mbuilder(exprs[ibrow]) assert is_zero(block) or isinstance(block, np.ndarray) blocks[ibrow, ibcol] = block if isinstance(block, np.ndarray): dtypes.append(block.dtype) return cl.array.to_device(queue, _bmat(blocks, dtypes))
def build_matrix(queue, places, exprs, input_exprs, domains=None, auto_where=None, context=None): """ :arg queue: a :class:`pyopencl.CommandQueue`. :arg places: a :class:`pytential.symbolic.execution.GeometryCollection`. Alternatively, any list or mapping that is a valid argument for its constructor can also be used. :arg exprs: an array of expressions corresponding to the output block rows of the matrix. May also be a single expression. :arg input_exprs: an array of expressions corresponding to the input block columns of the matrix. May also be a single expression. :arg domains: a list of discretization identifiers (see 'places') or *None* values indicating the domains on which each component of the solution vector lives. *None* values indicate that the component is a scalar. If *None*, *auto_where* or, if it is not provided, :class:`~pytential.symbolic.primitives.DEFAULT_SOURCE` is required to be a key in :attr:`places`. :arg auto_where: For simple source-to-self or source-to-target evaluations, find 'where' attributes automatically. """ if context is None: context = {} from pytools.obj_array import is_obj_array, make_obj_array if not isinstance(places, GeometryCollection): places = GeometryCollection(places, auto_where=auto_where) exprs = _prepare_expr(places, exprs) if not is_obj_array(exprs): exprs = make_obj_array([exprs]) try: input_exprs = list(input_exprs) except TypeError: # not iterable, wrap in a list input_exprs = [input_exprs] domains = _prepare_domains(len(input_exprs), places, domains, places._default_source_place) from pytential.symbolic.matrix import MatrixBuilder, is_zero nblock_rows = len(exprs) nblock_columns = len(input_exprs) blocks = np.zeros((nblock_rows, nblock_columns), dtype=np.object) dtypes = [] for ibcol in range(nblock_columns): mbuilder = MatrixBuilder( queue, dep_expr=input_exprs[ibcol], other_dep_exprs=(input_exprs[:ibcol] + input_exprs[ibcol + 1:]), dep_source=places[domains[ibcol]], dep_discr=places.get_discretization(domains[ibcol]), places=places, context=context) for ibrow in range(nblock_rows): block = mbuilder(exprs[ibrow]) assert is_zero(block) or isinstance(block, np.ndarray) blocks[ibrow, ibcol] = block if isinstance(block, np.ndarray): dtypes.append(block.dtype) return cl.array.to_device(queue, _bmat(blocks, dtypes))