def from_clusters(cls, *clusters): """ Build a new Cluster from a sequence of pre-existing Clusters with compatible IterationSpace. """ assert len(clusters) > 0 root = clusters[0] if not all(root.ispace.is_compatible(c.ispace) for c in clusters): raise ValueError("Cannot build a Cluster from Clusters with " "incompatible IterationSpace") if not all(root.guards == c.guards for c in clusters): raise ValueError("Cannot build a Cluster from Clusters with " "non-homogeneous guards") exprs = chain(*[c.exprs for c in clusters]) ispace = IterationSpace.union(*[c.ispace for c in clusters]) dspace = DataSpace.union(*[c.dspace for c in clusters]) guards = root.guards properties = {} for c in clusters: for d, v in c.properties.items(): properties[d] = normalize_properties(properties.get(d, v), v) try: syncs = normalize_syncs(*[c.syncs for c in clusters]) except ValueError: raise ValueError("Cannot build a Cluster from Clusters with " "non-compatible synchronization operations") return Cluster(exprs, ispace, dspace, guards, properties, syncs)
def stree_schedule(clusters): """ Arrange an iterable of Clusters into a ScheduleTree. """ stree = ScheduleTree() mapper = OrderedDict() for c in clusters: pointers = list(mapper) # Find out if any of the existing nodes can be reused index = 0 root = stree for it0, it1 in zip(c.itintervals, pointers): if it0 != it1: break root = mapper[it0] index += 1 if it0.dim in c.guards: break # The reused sub-trees might acquire some new sub-iterators for i in pointers[:index]: mapper[i].ispace = IterationSpace.union(mapper[i].ispace, c.ispace.project([i.dim])) # Nested sub-trees, instead, will not be used anymore for i in pointers[index:]: mapper.pop(i) # Add in Iterations for i in c.itintervals[index:]: root = NodeIteration(c.ispace.project([i.dim]), root, c.properties.get(i.dim)) mapper[i] = root # Add in Expressions NodeExprs(c.exprs, c.ispace, c.dspace, c.ops, c.traffic, root) # Add in Conditionals and Syncs, which chop down the reuse tree drop = None for k, v in [(UniteratedInterval, stree)] + list(mapper.items()): if drop: mapper.pop(k) if k.dim in c.syncs: node = NodeSync(c.syncs[k.dim]) v.last.parent = node node.parent = v drop = True if k.dim in c.guards: node = NodeConditional(c.guards[k.dim]) v.last.parent = node node.parent = v drop = True return stree
def from_clusters(cls, *clusters): """ Build a new Cluster from a sequence of pre-existing Clusters with compatible IterationSpace. """ assert len(clusters) > 0 root = clusters[0] assert all(root.ispace.is_compatible(c.ispace) for c in clusters) exprs = chain(*[c.exprs for c in clusters]) ispace = IterationSpace.union(*[c.ispace for c in clusters]) dspace = DataSpace.union(*[c.dspace for c in clusters]) return Cluster(exprs, ispace, dspace)
def from_clusters(cls, *clusters): """ Build a new Cluster from a sequence of pre-existing Clusters with compatible IterationSpace. """ assert len(clusters) > 0 root = clusters[0] if not all(root.ispace.is_compatible(c.ispace) for c in clusters): raise ValueError("Cannot build a Cluster from Clusters with " "incompatible IterationSpace") if not all(root.properties == c.properties for c in clusters): raise ValueError("Cannot build a Cluster from Clusters with " "non-homogeneous properties") exprs = chain(*[c.exprs for c in clusters]) ispace = IterationSpace.union(*[c.ispace for c in clusters]) dspace = DataSpace.union(*[c.dspace for c in clusters]) return Cluster(exprs, ispace, dspace, properties=root.properties)
def stree_schedule(clusters): """ Arrange an iterable of Clusters into a ScheduleTree. """ stree = ScheduleTree() prev = None mapper = DefaultOrderedDict(lambda: Bunch(top=None, bottom=None)) def attach_metadata(cluster, d, tip): if d in cluster.guards: tip = NodeConditional(cluster.guards[d], tip) if d in cluster.syncs: tip = NodeSync(cluster.syncs[d], tip) return tip for c in clusters: # Add in any Conditionals and Syncs outside of the outermost Iteration tip = attach_metadata(c, None, stree) if tip is stree: pointers = list(mapper) else: pointers = [] index = 0 for it0, it1 in zip(c.itintervals, pointers): if it0 != it1: break index += 1 d = it0.dim # The reused sub-trees might acquire new sub-iterators as well as # new properties mapper[it0].top.ispace = IterationSpace.union( mapper[it0].top.ispace, c.ispace.project([d])) mapper[it0].top.properties = normalize_properties( mapper[it0].top.properties, c.properties[it0.dim]) # Different guards or syncops cannot be further nested if c.guards.get(d) != prev.guards.get(d) or \ c.syncs.get(d) != prev.syncs.get(d): tip = mapper[it0].top tip = attach_metadata(c, d, tip) mapper[it0].bottom = tip break else: tip = mapper[it0].bottom # Nested sub-trees, instead, will not be used anymore for it in pointers[index:]: mapper.pop(it) # Add in Iterations, Conditionals, and Syncs for it in c.itintervals[index:]: d = it.dim tip = NodeIteration(c.ispace.project([d]), tip, c.properties.get(d)) mapper[it].top = tip tip = attach_metadata(c, d, tip) mapper[it].bottom = tip # Add in Expressions NodeExprs(c.exprs, c.ispace, c.dspace, c.ops, c.traffic, tip) # Prepare for next iteration prev = c return stree
def stree_schedule(clusters): """ Arrange an iterable of Clusters into a ScheduleTree. """ stree = ScheduleTree() prev = Cluster(None) mapper = DefaultOrderedDict(lambda: Bunch(top=None, bottom=None)) def reuse_metadata(c0, c1, d): return (c0.guards.get(d) == c1.guards.get(d) and c0.syncs.get(d) == c1.syncs.get(d)) def attach_metadata(cluster, d, tip): if d in cluster.guards: tip = NodeConditional(cluster.guards[d], tip) if d in cluster.syncs: tip = NodeSync(cluster.syncs[d], tip) return tip for c in clusters: index = 0 # Reuse or add in any Conditionals and Syncs outside of the outermost Iteration if not reuse_metadata(c, prev, None): tip = attach_metadata(c, None, stree) maybe_reusable = [] else: try: tip = mapper[prev.itintervals[index]].top.parent except IndexError: tip = stree maybe_reusable = prev.itintervals for it0, it1 in zip(c.itintervals, maybe_reusable): if it0 != it1: break index += 1 d = it0.dim # The reused sub-trees might acquire new sub-iterators as well as # new properties mapper[it0].top.ispace = IterationSpace.union( mapper[it0].top.ispace, c.ispace.project([d])) mapper[it0].top.properties = normalize_properties( mapper[it0].top.properties, c.properties[it0.dim]) # Different guards or SyncOps cannot further be nested if not reuse_metadata(c, prev, d): tip = mapper[it0].top tip = attach_metadata(c, d, tip) mapper[it0].bottom = tip break else: tip = mapper[it0].bottom # Nested sub-trees, instead, will not be used anymore for it in prev.itintervals[index:]: mapper.pop(it) # Add in Iterations, Conditionals, and Syncs for it in c.itintervals[index:]: d = it.dim tip = NodeIteration(c.ispace.project([d]), tip, c.properties.get(d, ())) mapper[it].top = tip tip = attach_metadata(c, d, tip) mapper[it].bottom = tip # Add in Expressions exprs = [] for conditionals, g in groupby(c.exprs, key=lambda e: e.conditionals): exprs = list(g) # Indirect ConditionalDimensions induce expression-level guards if conditionals: guard = And(*conditionals.values(), evaluate=False) parent = NodeConditional(guard, tip) else: parent = tip NodeExprs(exprs, c.ispace, c.dspace, c.ops, c.traffic, parent) # Prepare for next iteration prev = c return stree