def __call__(self, expr): from hedge.tools import with_object_array_or_scalar, is_zero def bind_one(subexpr): if is_zero(subexpr): return subexpr else: from hedge.optemplate.primitives import OperatorBinding return OperatorBinding(self, subexpr) return with_object_array_or_scalar(bind_one, expr)
def get_next_step(self, available_names, done_insns): from pytools import all, argmax2 available_insns = [ (insn, insn.priority) for insn in self.instructions if insn not in done_insns and all(dep.name in available_names for dep in insn.get_dependencies())] if not available_insns: raise self.NoInstructionAvailable from pytools import flatten discardable_vars = set(available_names) - set(flatten( [dep.name for dep in insn.get_dependencies()] for insn in self.instructions if insn not in done_insns)) # {{{ make sure results do not get discarded from hedge.tools import with_object_array_or_scalar from hedge.optemplate.mappers import DependencyMapper dm = DependencyMapper(composite_leaves=False) def remove_result_variable(result_expr): # The extra dependency mapper run is necessary # because, for instance, subscripts can make it # into the result expression, which then does # not consist of just variables. for var in dm(result_expr): from pymbolic.primitives import Variable assert isinstance(var, Variable) discardable_vars.discard(var.name) with_object_array_or_scalar(remove_result_variable, self.result) # }}} return argmax2(available_insns), discardable_vars
def get_next_step(self, available_names, done_insns): from pytools import all, argmax2 available_insns = [(insn, insn.priority) for insn in self.instructions if insn not in done_insns and all( dep.name in available_names for dep in insn.get_dependencies())] if not available_insns: raise self.NoInstructionAvailable from pytools import flatten discardable_vars = set(available_names) - set( flatten([dep.name for dep in insn.get_dependencies()] for insn in self.instructions if insn not in done_insns)) # {{{ make sure results do not get discarded from hedge.tools import with_object_array_or_scalar from hedge.optemplate.mappers import DependencyMapper dm = DependencyMapper(composite_leaves=False) def remove_result_variable(result_expr): # The extra dependency mapper run is necessary # because, for instance, subscripts can make it # into the result expression, which then does # not consist of just variables. for var in dm(result_expr): from pymbolic.primitives import Variable assert isinstance(var, Variable) discardable_vars.discard(var.name) with_object_array_or_scalar(remove_result_variable, self.result) # }}} return argmax2(available_insns), discardable_vars
def execute(self, exec_mapper, pre_assign_check=None): """If we have a saved, static schedule for this instruction stream, execute it. Otherwise, punt to the dynamic scheduler below. """ if self.last_schedule is None: return self.execute_dynamic(exec_mapper, pre_assign_check) context = exec_mapper.context id_to_future = {} next_future_id = 0 schedule_is_delay_free = True for discardable_vars, insn, new_future_count in self.last_schedule: for name in discardable_vars: del context[name] if isinstance(insn, self.EvaluateFuture): future = id_to_future.pop(insn.future_id) if not future.is_ready(): schedule_is_delay_free = False assignments, new_futures = future() del future else: assignments, new_futures = \ insn.get_executor_method(exec_mapper)(insn) for target, value in assignments: if pre_assign_check is not None: pre_assign_check(target, value) context[target] = value if len(new_futures) != new_future_count: raise RuntimeError("static schedule got an unexpected number " "of futures") for future in new_futures: id_to_future[next_future_id] = future next_future_id += 1 if not schedule_is_delay_free: self.last_schedule = None self.static_schedule_attempts -= 1 from hedge.tools import with_object_array_or_scalar return with_object_array_or_scalar(exec_mapper, self.result)
def rhs(t, u, u_neighbor): return compiled_op_template(u=u, nb_bdry_u=with_object_array_or_scalar(nb_bdry_permute, u_neighbor))
def rhs(t, u, u_neighbor): return compiled_op_template(u=u, nb_bdry_u=with_object_array_or_scalar( nb_bdry_permute, u_neighbor))
def apply_op(field): from hedge.tools import with_object_array_or_scalar return with_object_array_or_scalar(lambda f: bound_op(f=f), field)
def __call__(self, expr, type_hints={}): from hedge.optemplate.mappers.type_inference import TypeInferrer self.typedict = TypeInferrer()(expr, type_hints) # {{{ flux batching # Fluxes can be evaluated faster in batches. Here, we find flux # batches that we can evaluate together. # For each FluxRecord, find the other fluxes its flux depends on. flux_queue = self.get_contained_fluxes(expr) for fr in flux_queue: fr.dependencies = set(sf.flux_expr for sf in self.get_contained_fluxes(fr.flux_expr)) \ - set([fr.flux_expr]) # Then figure out batches of fluxes to evaluate self.flux_batches = [] admissible_deps = set() while flux_queue: present_batch = set() i = 0 while i < len(flux_queue): fr = flux_queue[i] if fr.dependencies <= admissible_deps: present_batch.add(fr) flux_queue.pop(i) else: i += 1 if present_batch: # bin batched operators by representative operator batches_by_repr_op = {} for fr in present_batch: batches_by_repr_op[fr.repr_op] = \ batches_by_repr_op.get(fr.repr_op, set()) \ | set([fr.flux_expr]) for repr_op, batch in batches_by_repr_op.iteritems(): self.flux_batches.append( self.FluxBatch( repr_op=repr_op, flux_exprs=list(batch))) admissible_deps |= set(fr.flux_expr for fr in present_batch) else: raise RuntimeError("cannot resolve flux evaluation order") # }}} # Once flux batching is figured out, we also need to know which # derivatives are going to be needed, because once the # rst-derivatives are available, it's best to calculate the # xyz ones and throw the rst ones out. It's naturally good if # we can avoid computing (or storing) some of the xyz ones. # So figure out which XYZ derivatives of what are needed. self.diff_ops = self.collect_diff_ops(expr) # Flux exchange also works better when batched. self.flux_exchange_ops = self.collect_flux_exchange_ops(expr) # Finally, walk the expression and build the code. result = IdentityMapper.__call__(self, expr) # Then, put the toplevel expressions into variables as well. from hedge.tools import with_object_array_or_scalar result = with_object_array_or_scalar(self.assign_to_new_var, result) return Code(self.aggregate_assignments(self.code, result), result)
def execute_dynamic(self, exec_mapper, pre_assign_check=None): """Execute the instruction stream, make all scheduling decisions dynamically. Record the schedule in *self.last_schedule*. """ schedule = [] context = exec_mapper.context next_future_id = 0 futures = [] done_insns = set() force_future = False while True: insn = None discardable_vars = [] # check futures for completion i = 0 while i < len(futures): future = futures[i] if force_future or future.is_ready(): futures.pop(i) insn = self.EvaluateFuture(future.id) assignments, new_futures = future() force_future = False break else: i += 1 del future # if no future got processed, pick the next insn if insn is None: try: insn, discardable_vars = self.get_next_step( frozenset(context.keys()), frozenset(done_insns)) except self.NoInstructionAvailable: if futures: # no insn ready: we need a future to complete to continue force_future = True else: # no futures, no available instructions: we're done break else: for name in discardable_vars: del context[name] done_insns.add(insn) assignments, new_futures = \ insn.get_executor_method(exec_mapper)(insn) if insn is not None: for target, value in assignments: if pre_assign_check is not None: pre_assign_check(target, value) context[target] = value futures.extend(new_futures) schedule.append((discardable_vars, insn, len(new_futures))) for future in new_futures: future.id = next_future_id next_future_id += 1 if len(done_insns) < len(self.instructions): print "Unreachable instructions:" for insn in set(self.instructions) - done_insns: print " ", insn raise RuntimeError("not all instructions are reachable" "--did you forget to pass a value for a placeholder?") if self.static_schedule_attempts: self.last_schedule = schedule from hedge.tools import with_object_array_or_scalar return with_object_array_or_scalar(exec_mapper, self.result)
def execute_dynamic(self, exec_mapper, pre_assign_check=None): """Execute the instruction stream, make all scheduling decisions dynamically. Record the schedule in *self.last_schedule*. """ schedule = [] context = exec_mapper.context next_future_id = 0 futures = [] done_insns = set() force_future = False while True: insn = None discardable_vars = [] # check futures for completion i = 0 while i < len(futures): future = futures[i] if force_future or future.is_ready(): futures.pop(i) insn = self.EvaluateFuture(future.id) assignments, new_futures = future() force_future = False break else: i += 1 del future # if no future got processed, pick the next insn if insn is None: try: insn, discardable_vars = self.get_next_step( frozenset(context.keys()), frozenset(done_insns)) except self.NoInstructionAvailable: if futures: # no insn ready: we need a future to complete to continue force_future = True else: # no futures, no available instructions: we're done break else: for name in discardable_vars: del context[name] done_insns.add(insn) assignments, new_futures = \ insn.get_executor_method(exec_mapper)(insn) if insn is not None: for target, value in assignments: if pre_assign_check is not None: pre_assign_check(target, value) context[target] = value futures.extend(new_futures) schedule.append((discardable_vars, insn, len(new_futures))) for future in new_futures: future.id = next_future_id next_future_id += 1 if len(done_insns) < len(self.instructions): print "Unreachable instructions:" for insn in set(self.instructions) - done_insns: print " ", insn raise RuntimeError( "not all instructions are reachable" "--did you forget to pass a value for a placeholder?") if self.static_schedule_attempts: self.last_schedule = schedule from hedge.tools import with_object_array_or_scalar return with_object_array_or_scalar(exec_mapper, self.result)