Interactive Optimization with the SDFG API

Frontends produce a baseline SDFG that mirrors the structure of the user’s source program. The Python SDFG API exposes every node, edge, and property of that graph as a regular Python object, so an SDFG can be optimized incrementally from a notebook or REPL session without recompiling the program after every change. This page collects the entry points most often needed when driving that loop by hand.

Loading and inspecting a baseline

Any SDFG produced by a frontend or saved to disk can be loaded interactively:

import dace
sdfg = dace.SDFG.from_file('matmul.sdfg')          # JSON or .sdfgz
sdfg.view()                                        # opens VS Code or Jupyter
sdfg.validate()                                    # raise on inconsistencies

The view() call renders the graph through the SDFV viewer. validate() will raise descriptive exceptions on common issues such as missing memlets, dangling connectors, or undefined symbols.

Operating on the graph

The SDFG is just a stateful graph object. The most common operations are:

  • Iterating: sdfg.states(), sdfg.all_nodes_recursive(), sdfg.arrays.items().

  • Locating nodes by predicate, e.g. [n for n, _ in sdfg.all_nodes_recursive() if isinstance(n, dace.nodes.MapEntry)].

  • Editing properties in place: changing schedule, storage, or data.

  • Saving and reloading checkpoints with save() followed by from_file().

Each of these is safe to do inside a live session (even a notebook), as the SDFG is a pure Python data structure until you call compile().

Applying transformations interactively

The transformation API provides three idioms for firing a transformation from Python:

  1. Pattern matching with apply_transformations() or apply_transformations_repeated() walks the graph, finds every match for a transformation class, and applies it. These methods accept a list of classes and an options dictionary, making them convenient for batch experiments.

  2. Direct application via the apply_to class method targets a specific subgraph by passing the matched nodes as keyword arguments. This is the form used inside notebooks when you already know the nodes you want to operate on:

    from dace.transformation.dataflow import MapFusion
    MapFusion.apply_to(sdfg, first_map_exit=exit1, array=array_node,
                       second_map_entry=entry2)
    
  3. Whole-program passes are run with apply_pass() (or apply_passes()) and operate on the SDFG globally. Common examples include ScalarToSymbolPromotion and TransientReuse.

In all three cases the SDFG is mutated in place, so chaining operations is just calling them in sequence. To revert an experiment, reload from the saved baseline.

Instrumentation feedback loop

Interactive optimization works best when paired with runtime instrumentation. Set instrument (or instrument) on the regions of interest, run the program once, and inspect InstrumentationReport to confirm whether the last transformation actually moved the needle. The recommended workflow is:

  1. Identify the hottest map or library node from a profile.

  2. Apply a transformation (or a small sequence) targeting it.

  3. Re-run; compare the new instrumentation report against the previous one.

  4. If the change helped, save the SDFG; otherwise reload and try a different sequence.

Composing with the auto-optimizer

The automatic heuristics (dace.transformation.auto.auto_optimize) are a useful starting point even when the goal is a fully manual schedule. A common pattern is to call auto_optimize once to establish a strong baseline, save the result, and then drive the remaining optimizations by hand with the API described above.

For more involved manipulations, such as scripting transformation searches, see SDFG Builder API and the samples/optimization directory in the repository.