Skip to content

Experimental feature: Schedule tree representation#2262

Open
romanc wants to merge 169 commits intospcl:mainfrom
romanc:romanc/stree-v2
Open

Experimental feature: Schedule tree representation#2262
romanc wants to merge 169 commits intospcl:mainfrom
romanc:romanc/stree-v2

Conversation

@romanc
Copy link
Contributor

@romanc romanc commented Jan 9, 2026

Description

Schedule trees are an alternative representation of code in DaCe. It is a tree of nodes that represent the execution order of the SDFG. The tree can be used to perform schedule transformations on the SDFG, i.e., erasing an empty if branch, or merging two consecutive for-loops.

A first version of the schedule tree representation was merged with PR #1145, which adds support for creating schedule trees from SDFGs. This PR builds on #1466 and brings a first version of the transformation from schedule tree to SDFG.

tbennun and others added 30 commits December 3, 2023 12:50
Author: @romanc

@romanc is on leave for the next few days, thus I have replayed the
changes made in spcl#1808 here for faster
turnaround.
)

Fixes a reported failure mode of scalar to symbol promotion.
Backport of PR spcl#1917.

Co-authored-by: Roman Cattaneo <1116746+romanc@users.noreply.github.com>
Backport of typo fix for `v1/maintenance` (see
spcl#1944).

@phschaad would you mind hitting the merge button?

Co-authored-by: Roman Cattaneo <1116746+romanc@users.noreply.github.com>
@romanc romanc changed the title WIP: Schedule Tree Experimental feature: Schedule tree representation Feb 16, 2026
romanc added a commit to GridTools/gt4py that referenced this pull request Feb 17, 2026
…aCe (#2458)

## Description

In this PR we pull a preliminary version of the `ScheduleTree` concept based on top of current mainline DaCe, e.g., what will be known as DaCe v2.x.

The work in this branch is based on PR spcl/dace#2262 in DaCe, which is functionally
complete for our use-cases, but not yet fully ready to merge down to mainline DaCe. Once spcl/dace#2262 is merged into mainline DaCe, we'll create a follow-up PR (which might simplify
the DaCe integration in GT4Py since now both, cartesian and next, are on a compatible version of DaCe).

## Requirements

- [x] All fixes and/or new features come with corresponding tests.
- [ ] Important design decisions have been documented in the appropriate
ADR inside the [docs/development/ADRs/](docs/development/ADRs/README.md)
folder.
  N/A
Copy link
Collaborator

@tbennun tbennun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great! I would like to see it more feature complete (as it is very close to being!) and I left some comments on the code. Maybe more preprocessing passes can make the implementations simpler

raise RuntimeError("Expected schedule tree root.")

for child in stree.children:
if id(child.parent) != id(stree):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should use child.parent is not stree - better and has similar effect

Comment on lines +26 to +66
@dataclass
class Context:
root: 'ScheduleTreeRoot'
current_scope: Optional['ScheduleTreeScope']

access_cache: Dict[Tuple[SDFGState, str], Dict[str, nodes.AccessNode]]
"""Per scope (hashed by id(scope_node) access_cache."""


class ContextPushPop:
"""Append the given node to the scope, then push/pop the scope."""

def __init__(self, ctx: Context, state: SDFGState, node: 'ScheduleTreeScope') -> None:
if ctx.current_scope is None and not isinstance(node, ScheduleTreeRoot):
raise ValueError("ctx.current_scope is only allowed to be 'None' when node it tree root.")

self._ctx = ctx
self._parent_scope = ctx.current_scope
self._node = node
self._state = state

cache_key = (state, id(node))
assert cache_key not in self._ctx.access_cache
self._ctx.access_cache[cache_key] = {}

def __enter__(self) -> None:
assert not self._ctx.access_cache[(self._state, id(
self._node))], "Expecting an empty access_cache when entering the context."

self._ctx.current_scope = self._node

def __exit__(
self,
exc_type: Optional[type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> None:
cache_key = (self._state, id(self._node))
assert cache_key in self._ctx.access_cache

self._ctx.current_scope = self._parent_scope
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are these classes? They are missing docstrings and their purpose

return [tn.StateBoundaryNode(), visited]
return visited

stree = NestedSDFGStateBoundaryInserter().visit(stree)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh dear, needs a RefSet state boundary inserter

:return: The newly created state.
"""
if behavior != StateBoundaryBehavior.STATE_TRANSITION:
raise NotImplementedError("Only STATE_TRANSITION is supported as StateBoundaryBehavior in this prototype.")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise NotImplementedError("Only STATE_TRANSITION is supported as StateBoundaryBehavior in this prototype.")
raise NotImplementedError("Only STATE_TRANSITION is currently supported as StateBoundaryBehavior.")

if behavior != StateBoundaryBehavior.STATE_TRANSITION:
raise NotImplementedError("Only STATE_TRANSITION is supported as StateBoundaryBehavior in this prototype.")

# TODO: Some boundaries (control flow, state labels with goto) could not be fulfilled with every
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# TODO: Some boundaries (control flow, state labels with goto) could not be fulfilled with every
# TODO: Some boundaries (control flow, state labels with goto, pending assignments) could not be fulfilled with every

after_state: Optional[ControlFlowBlock] = None,
*,
label: Optional[str] = None,
assignments: Optional[Dict] = None,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type hint incomplete (key, value?)

insertion order since python 3.7 (which we rely on in this function
too). Depending on code generation it could(TM) be that we can
weaken (best case remove) the corresponding check from the sdfg
validator.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check on the validator is on purpose (and we have seen cases where transformations trigger race conditions, e.g., in special cases where constant propagation modifies something). The semantics of the SDFG should be clear in that you should not make assignments that can cause a cycle or assume any order in the application of inter-state edge assignments. The only order is that conditions precede assignments.

romanc added 4 commits March 5, 2026 16:03
Avoid costly calls to `inspect` by adding a dummy `DebugInfo` object.
The call to `inspect` would anyway just return the line in the stree ->
sdfg translation layer. If we really wanted to have real `DebugInfo`,
we'd need a way to annotate the reads/writes in the stree and then
propagate this.
@romanc romanc force-pushed the romanc/stree-v2 branch 5 times, most recently from 948e333 to 4b22dc5 Compare March 16, 2026 09:12
romanc added 2 commits March 16, 2026 11:05
Much has changed on the pyFV3 / NDSL side that needs updating in this
workflow configuration.

We're using a temporary branch for pyFV3 since there are changes needed
and that side too 🙈.
@romanc romanc force-pushed the romanc/stree-v2 branch 8 times, most recently from 37255cd to a20292b Compare March 16, 2026 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants