Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
2c0f782
Give a nested SDFG the data descriptors of its parent's containers
tbennun Sep 16, 2026
2d5fad9
Propagate memlets through the views the contract introduces
tbennun Sep 16, 2026
4d00309
Build schedule trees from integrated nested SDFGs
tbennun Sep 16, 2026
52fb726
Nest Python programs without squeezing their arguments
tbennun Sep 16, 2026
d922b82
Restate nested SDFG connectors in the transformations that move conta…
tbennun Sep 16, 2026
f4c5a01
Generate code for nested SDFGs whose connectors are whole containers
tbennun Sep 16, 2026
e20bcc3
Convert gt4py's legacy nested SDFGs in the pyFV3 CI
tbennun Sep 16, 2026
7bc8159
Decide a scope's schedule by the containers outside it
tbennun Sep 18, 2026
3a23b4d
Merge branch 'main' into nested-sdfg
tbennun Sep 28, 2026
cd7a57c
Review fixes
tbennun Sep 30, 2026
6ef1879
Revert changes to test
tbennun Sep 30, 2026
a5fd526
More tests to ensure correctness endures
tbennun Sep 30, 2026
b3fc726
Move symbol mapping integration into integrate_nested_sdfg, thus remo…
tbennun Sep 30, 2026
bd926f7
Update/revert IR docs
tbennun Sep 30, 2026
37ede25
Merge branch 'main' into nested-sdfg
tbennun Sep 30, 2026
3173eb8
Fix tests, improve dealias for expressions such as "N": "N + 1"
tbennun Sep 30, 2026
a3f0a42
Fix inlining requiring integrate_nested_sdfg within
tbennun Sep 30, 2026
2cd2f61
Validate nested SDFG connectors in the parent's symbols
tbennun Oct 1, 2026
e216a1e
Merge branch 'main' into nested-sdfg
tbennun Oct 1, 2026
8954b7e
Compare nested SDFG connectors through is_equivalent(symbol_mapping=...)
tbennun Oct 1, 2026
b98d3b1
Merge branch 'main' into nested-sdfg
ThrudPrimrose Oct 2, 2026
660605a
Fix nested-SDFG symbol mapping in allocation placement and the no-vie…
ThrudPrimrose Oct 2, 2026
faebae5
Replace reflective attribute probes in the for-loop frontend with typ…
tbennun Oct 4, 2026
7167078
Revert the stream case of output connector type inference
tbennun Oct 4, 2026
1acb8ab
Clarify that code node locations are stored as strings
tbennun Oct 4, 2026
76fc865
Iterate states with all_states() instead of states()
tbennun Oct 4, 2026
12de776
Respect an explicitly passed empty symbolic replacement dictionary
tbennun Oct 4, 2026
d998df6
Remove the unused partial-data paths of nest_state_subgraph
tbennun Oct 4, 2026
bc3fc7d
Fix sequential clashes in safe_replace and test simultaneous symbol r…
tbennun Oct 4, 2026
54eb285
Update copyright years of modified files to 2026
tbennun Oct 4, 2026
93d32b0
Warn when InlineSDFG skips a nested SDFG with windowed connectors
tbennun Oct 4, 2026
10d0b63
Move function-local imports to the top of the module
tbennun Oct 4, 2026
da154a7
Resolve symbols once per state when converting and integrating nested…
tbennun Oct 4, 2026
928ed46
Take dealiased symbol types from the symbols defined at the nested SD…
tbennun Oct 4, 2026
2daa237
Name the parts of a device clone's window in GPUTransformLocalStorage
tbennun Oct 4, 2026
25656a4
Avoid scope-defined names when renaming an aliasing inner container
tbennun Oct 4, 2026
4e67531
Reject windowed connectors read and written through different windows
tbennun Oct 4, 2026
ec363fb
Rename names that shadow a widened window's symbols
tbennun Oct 4, 2026
22215cb
Compare nested and parent descriptors through the symbol mapping when…
tbennun Oct 4, 2026
91adf5f
Replace the symbols of a window's strides simultaneously
tbennun Oct 4, 2026
dfdf060
Guard the view exception of the copy size check against code node ends
tbennun Oct 4, 2026
4565808
Compare nested SDFG connectors through the symbol mapping when propag…
tbennun Oct 4, 2026
6bef060
Merge branch 'main' into nested-sdfg
tbennun Oct 4, 2026
d703828
Make the symbol mapping serialization test follow the nested SDFG con…
tbennun Oct 4, 2026
18154d8
Index reduction gradients through the forward memlets
tbennun Oct 4, 2026
30653c4
Remove identity views written by library nodes
tbennun Oct 4, 2026
bc47bc5
Report duplicate memlet subsets at the state's control flow region
tbennun Oct 4, 2026
7c7371b
Check the GPUAuto reduction views against the nested SDFG contract
tbennun Oct 4, 2026
d87d4f3
Give the hand-built nested SDFG of the invariant dimension test one w…
tbennun Oct 4, 2026
00da59a
Keep the offset of integrated connectors in MapFission and propagation
tbennun Oct 4, 2026
b60b563
Inline connectors with their own offset through a view
tbennun Oct 4, 2026
cacd800
Match symbols by name when restating descriptors through a symbol map…
tbennun Oct 4, 2026
e332ad7
Fold symbol mappings into nested SDFGs with the parent's symbol types
tbennun Oct 4, 2026
9cabc8b
Report invalid return values with the SDFG declaring them
tbennun Oct 4, 2026
da2948b
Import numpy.typing unconditionally
tbennun Oct 4, 2026
ee0d1a2
Keep callbacks untyped when folding symbol mappings
tbennun Oct 4, 2026
4968a05
Add per-SDFG symbol facts and an explicit-facts prover
ThrudPrimrose Oct 5, 2026
833087f
Merge remote-tracking branch 'origin/main' into symbol-registry
ThrudPrimrose Oct 5, 2026
5498ceb
Prove symbol facts through relations only and accept symbols in add_s…
ThrudPrimrose Oct 5, 2026
4f373e4
Derive the facts at a block or node and prove integer powers with them
ThrudPrimrose Oct 5, 2026
3dbfc0f
Keep scoped symbols out of the SDFG symbol table
ThrudPrimrose Oct 5, 2026
e62b75e
Merge remote-tracking branch 'origin/main' into symbol-registry
ThrudPrimrose Oct 5, 2026
17e46ce
Bound int_floor, int_ceil and Mod, and split Min and Max, in the fact…
ThrudPrimrose Oct 5, 2026
09f6d37
Serialize symbols as bare names and the SDFG's symbols as a SymbolRepo
ThrudPrimrose Oct 5, 2026
ba6d7f7
Make SDFG.symbols a read-only view of the repository parameters
ThrudPrimrose Oct 5, 2026
8092352
Validate the facts of symbol scopes that carry facts; test the read-o…
ThrudPrimrose Oct 5, 2026
4fa5847
Never declare loop or map iterators as SDFG symbols
ThrudPrimrose Oct 5, 2026
2054f31
Merge the SymbolRepo serialization and fix scoped symbols in consumers
ThrudPrimrose Oct 5, 2026
eff6806
Merge remote-tracking branch 'origin/main' into symbol-registry
ThrudPrimrose Oct 6, 2026
f3e9dd7
Derive symbol scopes from the graph; the repository keeps only declar…
ThrudPrimrose Oct 6, 2026
0b22c60
Validate that an SDFG declares its free symbols, and declare them whe…
ThrudPrimrose Oct 6, 2026
5c1cfd7
Type a dynamic scope input by its data while its connector is untyped
ThrudPrimrose Oct 6, 2026
c2c2c79
Run the ADI CPU test on sequential maps
ThrudPrimrose Oct 6, 2026
0cbf8fb
Identify a symbol by its name and pass facts to symbolic reasoning
ThrudPrimrose Oct 6, 2026
d4c99c1
Type symbols by the SDFG, and fix what name-only identity broke
ThrudPrimrose Oct 6, 2026
a2764ee
Type the callbacks and operands a frontend check reads from their dec…
ThrudPrimrose Oct 6, 2026
481fabd
Find the GPU code of a test by its name and the runtime API by the ba…
ThrudPrimrose Oct 6, 2026
84d18e7
Merge commit '20db93463' into symbol-registry
ThrudPrimrose Oct 8, 2026
c49886f
Merge commit '1a97f7d67' into symbol-registry
ThrudPrimrose Oct 8, 2026
5f42269
Merge commit '691978056' (ruff reformat) into symbol-registry; reform…
ThrudPrimrose Oct 8, 2026
9bd92bd
Merge remote-tracking branch 'origin/main' into symbol-registry
ThrudPrimrose Oct 8, 2026
51bdeb9
Remove a merge leftover
ThrudPrimrose Oct 8, 2026
3fdc96c
Read an order comparison as a relation in one place; an unparsable gu…
ThrudPrimrose Oct 8, 2026
1d6e800
Pin the readers of a symbol object's declaration to the frontend boun…
ThrudPrimrose Oct 8, 2026
ed5b89b
Bind the names the frontend assigns on interstate edges by the edge, …
ThrudPrimrose Oct 8, 2026
1354309
Divisibility facts: a relation that one expression divides another, a…
ThrudPrimrose Oct 8, 2026
f495479
A nested SDFG's assumptions on its mapped symbols must be provable at…
ThrudPrimrose Oct 8, 2026
4a69a06
Type a name an edge of a nested subgraph assigns by its edge when not…
ThrudPrimrose Oct 8, 2026
668bf19
An undefined placeholder dimension gives no facts
ThrudPrimrose Oct 8, 2026
6da9f4a
Subset coverage, intersection and union prove under explicit facts
ThrudPrimrose Oct 9, 2026
4cb6256
Overlap answers are a Truth; memlet sets and schedule trees carry fac…
ThrudPrimrose Oct 9, 2026
d6a1fdf
bounding_box_union folds its Min/Max with symbolic.fold_extrema; shar…
ThrudPrimrose Oct 9, 2026
0b11a72
Merge origin/main (ruff lint #2671) into symbol-registry; lint the br…
ThrudPrimrose Oct 9, 2026
e122ecd
Propagation patterns decide signs and monotonicity from facts
ThrudPrimrose Oct 9, 2026
cf56021
Producers of hard-coded symbols declare them in the registry
ThrudPrimrose Oct 9, 2026
f790a6d
LoopLocalMemoryReduction proves its window condition from facts; drop…
ThrudPrimrose Oct 9, 2026
306f4d6
MoveLoopIntoMap proves an index injective from facts
ThrudPrimrose Oct 9, 2026
316417f
LoopToMap warns when it assumes a symbolic step is positive
ThrudPrimrose Oct 9, 2026
050ea9b
Schedule tree roots default to no facts; propagation simplifies under…
ThrudPrimrose Oct 9, 2026
f973599
Autodiff indexes its symbolic tasklets by DaCe symbols
ThrudPrimrose Oct 9, 2026
11c80de
Validation requires declarations for the symbols an SDFG uses, not fo…
ThrudPrimrose Oct 9, 2026
025f2b6
Merge remote-tracking branch 'origin/main' into HEAD
ThrudPrimrose Oct 10, 2026
01251fd
SMALL symbol scale: << implies <, checked on every call
ThrudPrimrose Oct 12, 2026
e34b7c2
Merge remote-tracking branch 'origin/main' into symbol-registry
ThrudPrimrose Oct 12, 2026
189e9c3
Merge remote-tracking branch 'origin/main' into symbol-registry
ThrudPrimrose Oct 12, 2026
7856b68
Merge branch 'symbol-scales' into symbol-registry
ThrudPrimrose Oct 12, 2026
a8d6a97
Call states() in the symbol repository, as main does since all_states…
ThrudPrimrose Oct 12, 2026
659814d
Release a shape-declared symbol when a loop or map scope binds it
ThrudPrimrose Oct 13, 2026
b7a9886
Merge remote-tracking branch 'origin/main' into symbol-registry
ThrudPrimrose Oct 13, 2026
648819b
Fix ten symbol-registry review findings
ThrudPrimrose Oct 13, 2026
88205a5
Fix the second round of symbol-registry review findings
ThrudPrimrose Oct 13, 2026
871c833
Do not propagate a constant range through an expression that does not…
ThrudPrimrose Oct 13, 2026
731fb6c
Declare a shape symbol with the dtype of its object again; LoopToMap …
ThrudPrimrose Oct 13, 2026
c2a4052
Check constant-range bounds that are plain numbers without substituti…
ThrudPrimrose Oct 13, 2026
719862c
State that a symbol name has one dtype in a process
ThrudPrimrose Oct 13, 2026
a450786
Fixes found porting extended onto the registry, each with a reproducer
ThrudPrimrose Oct 15, 2026
1f681c1
Frontend: an annotation's free symbol never replaces the program's de…
ThrudPrimrose Oct 15, 2026
7f3e5d2
Merge remote-tracking branch 'origin/main' into symreg-backports
ThrudPrimrose Oct 16, 2026
b0047c4
RelaxIntegerPowers drops the rounding around a power it relaxes to an…
ThrudPrimrose Oct 16, 2026
149cae1
Test a relation whose only extremum sits inside a division
ThrudPrimrose Oct 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions dace/autodiff/data_forwarding/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,11 +540,10 @@ def _get_assign_tasklet(
memlet_access_iterators.append(str(element[0]))
else:
# This is a range tuple we need to add an iterator for
# Create a random new free symbol
free_symbol = forward_state.sdfg.find_new_symbol("si")

# Add the new symbol here so that find_new_symbol doesn't return it again
forward_state.sdfg.add_symbol(free_symbol, dtypes.int64)
# A map parameter that neither the SDFG nor an earlier dimension uses
free_symbol, index = "si", 0
while free_symbol in param_dict or forward_state.sdfg.is_name_used(free_symbol):
free_symbol, index = f"si_{index}", index + 1
memlet_access_iterators.append(free_symbol)
param_dict.update({free_symbol: element})

Expand Down
9 changes: 3 additions & 6 deletions dace/autodiff/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,10 @@ def connector_symbol(name: str, dtype: dtypes.typeclass | None = None) -> symbol

Always a ``symbolic.symbol``, never a bare ``sympy.Symbol``: the two compare unequal while
still substituting for one another, which is what makes ``diff()`` return a silent zero.
``integer=None`` where the dtype is unknown, so that ``symbol``'s int32 default does not
assert an integer assumption the declaration never made.
"""
if isinstance(dtype, dtypes.typeclass):
return symbolic.symbol(name, dtype)
return symbolic.symbol(name, integer=None)
return symbolic.symbol(name)


def backward_symbol_mapping(nested_sdfg: SDFG, parent_state: SDFGState) -> dict[str, symbolic.symbol]:
Expand Down Expand Up @@ -460,9 +458,8 @@ def code_to_exprs(
raise AutoDiffException(f"Expected connector '{conn}' to be in indexed objects map for pointer type")
indexed_objects_code += f" {conn} = sp.IndexedBase('{conn}')\n"
for idx in indexed_objects_map[conn]:
idx_sym = connector_symbol(idx, symbols.get(idx) or tasklet.in_connectors.get(idx))
# An Idx label must be integral, so a non-integer declaration cannot supply one.
symbol_table[idx] = sp.Idx(idx_sym if idx_sym.is_integer else symbolic.symbol(idx))
# An index of an SDFG symbol keeps its entry; sympy indexes an IndexedBase by any expression
symbol_table.setdefault(idx, connector_symbol(idx))
indexed_objects_code += f" {idx} = {SYMBOL_TABLE_GLOBAL}['{idx}']\n"

code_fn = """
Expand Down
30 changes: 30 additions & 0 deletions dace/codegen/compiled_sdfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,15 @@ def __init__(self, sdfg, lib: ReloadableDLL, argnames: list[str] | None = None):
# Present exactly when a GPU target emitted its init/exit pair, which is a sharper test than
# the ``has_gpu_code`` heuristic below.
self._gpu_last_error = self.get_exported_function("__dace_gpu_last_error", restype=ctypes.c_int)
# Avoid import loop
from dace.sdfg.symbol_repo import program_assumptions

# The orders proofs took from symbol scales, checked against the symbol values of every call
self._assumptions = program_assumptions(sdfg)
self._check_assumptions = (
lib.get_symbol(f"__dace_check_assumptions_{sdfg.name}", restype=ctypes.c_int) if self._assumptions else None
)
self._initarg_names: tuple[str, ...] = ()

# Cache SDFG return values
self._return_syms: dict[str, Any] = None
Expand Down Expand Up @@ -608,6 +617,10 @@ def fast_call(
# Call initializer function if necessary, then SDFG
if self._initialized is False:
self._lib.load()
# Checked with the library loaded: a failed call unloads it
if self._check_assumptions is not None:
self._raise_on_broken_assumption(initargs)
if self._initialized is False:
self._initialize(initargs)

with hooks.invoke_compiled_sdfg_call_hooks(self, callargs):
Expand All @@ -628,6 +641,22 @@ def fast_call(
self._initialized = False
raise

def _raise_on_broken_assumption(self, initargs: Sequence[Any]) -> None:
"""Raises if the symbol values break an order a transformation took from the symbol scales."""
broken = self._check_assumptions(*initargs)
if broken == 0:
return
relation = self._assumptions[broken - 1]
values = ", ".join(
f"{name}={getattr(value, 'value', value)}"
for name, value in zip(self._initarg_names, initargs)
if name in symbolic.relation_names(relation)
)
raise ValueError(
f'"{self._sdfg.name}" was optimized assuming {relation.lhs} < {relation.rhs} (SMALL symbols are much '
f"smaller than the others), which does not hold for {values}"
)

def __del__(self):
if self._initialized is True:
self.finalize()
Expand Down Expand Up @@ -722,6 +751,7 @@ def construct_arguments(self, *args: Any, **kwargs: Any) -> tuple[tuple[Any], tu
)
newargs = tuple(carg for carg, _aname in callparams)
initargs = tuple(carg for carg, aname in callparams if aname in symbols)
self._initarg_names = tuple(aname for _carg, aname in callparams if aname in symbols)

return (newargs, initargs)

Expand Down
11 changes: 6 additions & 5 deletions dace/codegen/instrumentation/data/data_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,16 @@ def __getitem__(self, item: str) -> ArrayLike | Number | list[ArrayLike] | list[
nparr, view = self._read_array_file(file, npdtype)
self.loaded_values[item, i] = nparr
results.append(view)
elif item in self.sdfg.symbols:
dtype: dtypes.typeclass = self.sdfg.symbols[item]
npdtype = dtype.as_numpy_dtype()
else:
# The dump saves the symbols each state defines, which include loop variables and map parameters
types = {name: dtype for state in self.sdfg.states() for name, dtype in state.defined_symbols().items()}
if item not in types:
raise KeyError(f"Item {item} not found in report")
npdtype = types[item].as_numpy_dtype()
for i, file in enumerate(filenames):
val = self._read_symbol_file(file, npdtype)
self.loaded_values[item, i] = val
results.append(val)
else:
raise KeyError(f"Item {item} not found in report")

if len(results) == 1:
return results[0]
Expand Down
11 changes: 7 additions & 4 deletions dace/codegen/targets/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from dace.sdfg import SDFG, SDFGState, is_devicelevel_gpu, nodes, propagation
from dace.sdfg import graph as gr
from dace.sdfg import utils as sdutil
from dace.sdfg.state import ControlFlowRegion, StateSubgraphView
from dace.sdfg.state import ControlFlowRegion, StateSubgraphView, SymbolResolver

if TYPE_CHECKING:
from dace.codegen.dispatcher import TargetDispatcher
Expand Down Expand Up @@ -424,7 +424,7 @@ def reshape_strides(subset, strides, original_strides, copy_shape):
reduced_tile_sizes += [1] * (dims - len(reduced_tile_sizes)) # Pad the remainder with 1s to maintain dimensions.

reshaped_copy = copy_shape + [ts for ts in subset.tile_sizes if ts != 1]
reshaped_copy[: len(copy_shape)] = [s // ts for s, ts in zip(copy_shape, reduced_tile_sizes)]
reshaped_copy[: len(copy_shape)] = [symbolic.int_floor(s, ts) for s, ts in zip(copy_shape, reduced_tile_sizes)]

new_strides = [0] * len(reshaped_copy)
elements_remaining = functools.reduce(sp.Mul, copy_shape, 1)
Expand Down Expand Up @@ -697,6 +697,8 @@ def _check_neighbor_conflicts(dfg, edge):
Returns True if there are no conflicts, False if there may be.
"""
outer = propagation.propagate_memlet(dfg, edge.data, edge.dst, False)
# The propagated subsets are outside the map
facts = SymbolResolver().facts_at(dfg, dfg.entry_node(edge.dst))
siblings = dfg.in_edges(edge.dst)
for sibling in siblings:
if sibling is edge:
Expand All @@ -705,7 +707,7 @@ def _check_neighbor_conflicts(dfg, edge):
continue
# Check if there is definitely no overlap in the propagated memlet
sibling_outer = propagation.propagate_memlet(dfg, sibling.data, edge.dst, False)
if subsets.intersects(outer.subset, sibling_outer.subset) == False:
if subsets.intersects(outer.subset, sibling_outer.subset, facts) is symbolic.Truth.FALSE:
# In that case, continue
continue

Expand Down Expand Up @@ -798,9 +800,10 @@ def is_write_conflicted_with_reason(dfg, edge, datanode=None, sdfg_schedule=None
dst = sdutil.get_last_view_node(dfg, dst) or dst

if dfg.in_degree(dst) > 0:
facts = SymbolResolver().facts_at(dfg, dst)
for x, y in itertools.combinations(dfg.in_edges(dst), 2):
x, y = x.data.subset, y.data.subset
if subsets.intersects(x, y):
if subsets.intersects(x, y, facts) is symbolic.Truth.TRUE:
return dst

# If this is a nested SDFG and the access leads outside
Expand Down
27 changes: 16 additions & 11 deletions dace/codegen/targets/cuda.py
Original file line number Diff line number Diff line change
Expand Up @@ -2796,7 +2796,7 @@ def generate_kernel_scope(
# Generate all index arguments for kernel grid
krange = subsets.Range(kernel_map.range[::-1])
kdims = krange.size()
dsym = [symbolic.symbol("__DAPB%d" % i, nonnegative=True, integer=True) for i in range(len(krange))]
dsym = [symbolic.symbol("__DAPB%d" % i) for i in range(len(krange))]
bidx = krange.coord_at(dsym)

# handle dynamic map inputs
Expand Down Expand Up @@ -2893,7 +2893,7 @@ def generate_kernel_scope(
condition = ""

# Optimize conditions if they are always true
if i >= 3 or (dsym[i] >= minel) != True:
if i >= 3 or not _index_starts_at(dsym[i], minel):
condition += f"{v} >= {_topy(minel)}"
# The grid of the distributed dimension is padded to a multiple of the number of
# chiplets, so its trailing blocks always have to be masked out
Expand Down Expand Up @@ -3185,10 +3185,7 @@ def generate_devicelevel_scope(

device_map_range = subsets.Range(scope_map.range[::-1])
device_map_dims = device_map_range.size()
dsym = [
symbolic.symbol("__DAPB%d" % i, nonnegative=True, integer=True)
for i in range(len(device_map_range))
]
dsym = [symbolic.symbol("__DAPB%d" % i) for i in range(len(device_map_range))]
bidx = device_map_range.coord_at(dsym)

# handle dynamic map inputs
Expand Down Expand Up @@ -3262,7 +3259,7 @@ def generate_devicelevel_scope(
condition = ""

# Optimize conditions if they are always true
if i >= 3 or (dsym[i] >= minel) != True:
if i >= 3 or not _index_starts_at(dsym[i], minel):
condition += f"{v} >= {_topy(minel)}"
if (
i >= 3
Expand Down Expand Up @@ -3315,7 +3312,7 @@ def generate_devicelevel_scope(
brange = subsets.Range(scope_map.range[::-1])
kdims = brange.size()
dsym = [
symbolic.symbol("__DAPT%d" % i, nonnegative=True, integer=True) - off
symbolic.symbol("__DAPT%d" % i) - off
for i, off in zip(range(len(brange)), extra_gdim_offsets[scope_map])
]
gdims = len(self._kernel_map.params)
Expand Down Expand Up @@ -3352,7 +3349,7 @@ def generate_devicelevel_scope(
#############################################

# Block range start
if i >= 3 or (dsym[i] >= minel) != True:
if i >= 3 or not _index_starts_at(dsym[i], minel):
condition += f"{v} >= {_topy(minel)}"

# Special case: block size is exactly the range of the map (0:b)
Expand Down Expand Up @@ -3389,7 +3386,7 @@ def generate_devicelevel_scope(

brange = subsets.Range(scope_map.range[::-1])
kdims = brange.size()
dsym = [symbolic.symbol("__DAPT%d" % i, nonnegative=True, integer=True) for i in range(len(brange))]
dsym = [symbolic.symbol("__DAPT%d" % i) for i in range(len(brange))]
dsym_end = [d + (bs * rng[2]) - 1 for d, bs, rng in zip(dsym, self._block_dims, brange)]
tidx = brange.coord_at(dsym)

Expand Down Expand Up @@ -3434,7 +3431,7 @@ def generate_devicelevel_scope(
#############################################

# Block range start
if i >= 3 or (dsym[i] >= minel) != True:
if i >= 3 or not _index_starts_at(dsym[i], minel):
condition += f"{v} >= {_topy(minel)}"

# Special case: block size is exactly the range of the map (0:b)
Expand Down Expand Up @@ -3759,6 +3756,14 @@ def emit_interstate_variable_declaration(
# Helper functions and classes


def _index_starts_at(index: sympy.Expr, begin: sympy.Expr) -> bool:
"""Whether a grid index (of ``__DAPB*`` block and ``__DAPT*`` thread index symbols, which count up from zero)
provably never falls below ``begin``."""
names = frozenset(s.name for s in index.free_symbols if s.name.startswith(("__DAPB", "__DAPT")))
from_zero = [symbolic.Relation(symbolic.RelationKind.LE, sympy.Integer(0), symbolic.symbol(name)) for name in names]
return symbolic.provably_le(begin, index, symbolic.Facts(from_zero, names))


def _topy(arr):
"""Converts an array of symbolic variables (or one) to C++ strings."""
if not isinstance(arr, list):
Expand Down
16 changes: 13 additions & 3 deletions dace/codegen/targets/framecode.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Copyright 2019-2026 ETH Zurich and the DaCe authors. All rights reserved.
import collections
import copy
import pathlib
import re
from typing import Any
Expand All @@ -19,6 +18,7 @@
from dace.sdfg import scope as sdscope
from dace.sdfg.analysis import cfg as cfg_analysis
from dace.sdfg.state import AbstractControlFlowRegion, ControlFlowBlock, ControlFlowRegion, LoopRegion, SymbolResolver
from dace.sdfg.symbol_repo import assumption_check, program_assumptions
from dace.sdfg.type_inference import infer_expr_type
from dace.transformation.passes.analysis import StateReachability, loop_analysis

Expand Down Expand Up @@ -342,12 +342,22 @@ def generate_footer(self, sdfg: SDFG, global_stream: CodeIOStream, callsite_stre
sdfg,
)

# The orders proofs took from symbol scales; init refuses symbol values that break one
assumptions = program_assumptions(sdfg)
if assumptions:
readable = {name for name in self.free_symbols(sdfg) if not name.startswith("__dace")}
callsite_stream.write(assumption_check(sdfg.name, initparams, assumptions, readable), sdfg)

callsite_stream.write(
f"""
DACE_EXPORTED {mangle_dace_state_struct_name(sdfg)} *__dace_init_{sdfg.name}({initparams})
{{""",
sdfg,
)
if assumptions:
callsite_stream.write(
f" if (__dace_check_assumptions_{sdfg.name}({initparamnames}) != 0) return nullptr;\n", sdfg
)

# Invoke all instrumentation providers
for instr in self._dispatcher.instrumentation.values():
Expand Down Expand Up @@ -598,7 +608,7 @@ def dispatch_state(state: SDFGState) -> str:
states_generated.add(state) # For sanity check
return stream.getvalue()

callsite_stream.write(cflow.control_flow_region_to_code(sdfg, dispatch_state, self, sdfg.symbols), sdfg)
callsite_stream.write(cflow.control_flow_region_to_code(sdfg, dispatch_state, self, dict(sdfg.symbols)), sdfg)

opbar.done()

Expand Down Expand Up @@ -1061,7 +1071,7 @@ def generate_code(
self.dispatcher.defined_vars.add(cname, disp.DefinedType.Scalar, ctype.dtype.ctype)

# Allocate inter-state variables
global_symbols = copy.deepcopy(sdfg.symbols)
global_symbols = dict(sdfg.symbols)
global_symbols.update({aname: arr.dtype for aname, arr in sdfg.arrays.items()})
interstate_symbols = {}
for cfr in sdfg.all_control_flow_regions():
Expand Down
9 changes: 0 additions & 9 deletions dace/config_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,6 @@ required:
"for" and "while" from state transitions, allowing
code generators to generate appropriate code.

symbolic_positive:
type: bool
default: true
title: Treat all symbolic expressions as positive
description: >
Every expression in which a symbolic value appears
is treated as strictly positive. This is necessary
for certain Range evaluations using Subgraph Fusion.

match_exception:
type: bool
default: false
Expand Down
Loading
Loading