Pattern-Matching and Subgraph Transformations
In DaCe, the easiest way to locally modify an SDFG is by data-centric transformations. Transformations are a powerful tool to optimize applications in DaCe. You can go from naive code to state-of-the-art performance using only transformations.
All transformations extend the TransformationBase
class. There are three built-in types of transformations in DaCe:
Pattern-matching Transformations (extending
PatternTransformation
): Transformations that require a certain subgraph structure to match. Within this abstract class, there are two sub-classes:
SingleStateTransformation
: Patterns are limited to a single SDFG state.
MultiStateTransformation
: Patterns are given on a subgraph of an SDFG state machine.A pattern-matching must extend at least one of those two classes.
Subgraph Transformations (extending
SubgraphTransformation
): Transformations that can operate on arbitrary subgraphs.Another form of (implicit) transformation is a Library node expansion (extending
ExpandTransformation
). It is a class used for tracking when library nodes are expanded, and creating a library node implementation involves extending this class.
Transformations can have properties and those can be used when applying them: for example, tile sizes in MapTiling
.
For more information on how to use and author data-centric transformations, see the Using and Creating Transformations tutorial.
Pattern-Matching Transformations
A pattern-matching transformation works on a specific subgraph pattern, and, using the API, can be used to find all
instances of that pattern and apply it anywhere.
Authoring such a transformation requires extending one of the two subclasses mentioned above
(SingleStateTransformation
or MultiStateTransformation
), add static PatternNode
fields to the class to
represent the pattern, and implement at least three methods:
expressions
: A method that returns a list of graph patterns that match this transformation.
can_be_applied
: A method that, given a subgraph candidate, checks for additional conditions whether it can be transformed.
apply
: A method that applies the transformation on the given SDFG.
An instance of the transformation class is associated with a specific match, so using the fields in the class relate to a specific subgraph.
For example, the following sample transformation matches an access node connected to a map entry, and changes the map’s label to match the name of that access node:
from dace.sdfg import nodes, SDFG, SDFGState
from dace.sdfg.utils import node_path_graph
from dace.transformation import transformation as xf
class MyTransformation(xf.SingleStateTransformation):
# Pattern nodes are defined here and can be used in the class
access = xf.PatternNode(nodes.AccessNode)
map_node = xf.PatternNode(nodes.MapEntry)
@classmethod
def expressions(cls):
# The pattern to match is ``access -> map_node``. Since this is a
# class method, accessing ``cls.access`` gets the pattern node.
return [node_path_graph(cls.access, cls.map_node)]
# Because this is a Single-State Transformation, the first argument here
# is ``state``
def can_be_applied(self, state: SDFGState, expr_index: int, sdfg: SDFG,
permissive=False) -> bool:
# We can now use ``self.access``, which refers to a specific subgraph
# pattern match
if self.access.data == 'mydata':
return True
# We only match patterns in which the access node is accessing 'mydata'
return False
def apply(self, state: SDFGState, sdfg: SDFG) -> nodes.MapEntry:
# Here we apply the transformation, and can return any object. This
# is sometimes used when transformations are composed together and
# need to pass information to each other.
self.map_node.label = 'mymap'
return self.map_node
Subgraph Transformations
Subgraph transformations can be applied to any subgraph that returns True for the can_be_applied
method. It is used
when arbitrary local regions need to be modified, e.g., in SubgraphFusion
. The implementation is very similar to
pattern-matching transformations, but without the pattern. A simple example with a property would be:
from dace.sdfg import nodes, SDFG
from dace.sdfg.utils import node_path_graph
from dace.transformation import transformation as xf
from dace.sdfg.graph import SubgraphView
from dace.properties import make_properties, Property
@make_properties
class ExampleSubgraphXform(xf.SubgraphTransformation):
"""
This string describes the transformation and will be shown in the Visual Studio Code plugin.
"""
# Properties can be defined on Transformation classes as with other objects
simplify = Property(desc="Simplify SDFG after applying transformation.", dtype=bool, default=False)
def can_be_applied(self, sdfg: SDFG, subgraph: SubgraphView) -> bool:
return True
def apply(self, sdfg: SDFG) -> None:
# First we obtain the subgraph view from the SDFG we matched in
subgraph = self.subgraph_view(sdfg)
# Then we can work on the graph normally
for node in subgraph.nodes():
# Do something complex...
pass
if self.simplify:
sdfg.simplify()