DaCe python-frontend issues found sweeping 576 autogenerated programs (2026-08-06)
Measured in ~/Work/optarena against ~/Work/dace @ ecad3253f (extended). Every program is
AUTOGENERATED from a numpy reference, so each of these is a construct the generator emits and the
kernel silently loses its DaCe column over. Filed, not fixed (this session's scope).
1. A runtime scalar is re-promoted to a FRESH symbol at every shape/slice use (64 kernels)
The single largest cause. A padded workspace is allocated from a scalar and then written through a
slice built from the SAME scalar:
padded = np.zeros((n, c, h + 2 * padding, w + 2 * padding), dtype=x.dtype)
padded[:, :, padding:padding + h, padding:padding + w] = x
The frontend promotes each USE of padding to its own __sym_<name>_<k>, so the write's extent
becomes __sym_padded_slice - __sym_padding and cannot be proven equal to x's height:
IndexError: could not broadcast input array from shape [batch_size, in_channels, height, width]
into shape [__sym___inl1_n_0, __sym___inl1_c_in_0,
__sym___inl1_padded_slice - __sym___inl1_padding, ...]
Reusing one promoted symbol per scalar VERSION would make equal-by-construction extents provable.
Reachable via dace/frontend/python/replacements/utils.py:169 broadcast_together.
2. A chained comparison raises a BODYLESS NotImplementedError (48 kernels)
dace/frontend/python/newast.py:5349:
def visit_Compare(self, node: ast.Compare):
if len(node.ops) > 1 or len(node.comparators) > 1:
raise NotImplementedError
if 0 <= oy < oh: is the single commonest bounds test in ported conv/pool code. Two separate
defects: the feature is missing, AND the refusal names neither the construct nor the file. An
unsupported construct must name itself. (Worked around in the generator by splitting the chain,
but only where the middle operand is a Name/Constant -- the split evaluates it twice.)
3. numpy.matmul has no SDFG implementation registered (17 kernels)
DaceSyntaxError: Function "numpy.matmul" is not registered with an SDFG implementation. np.dot
and the @ operator work; np.matmul does not, which reads as an oversight rather than a
decision.
4. Assigning a module-level dc.symbol inside a program crashes with a bare KeyError (10 kernels)
npwx, nvec, nvecx, npol = (int(npwx), ...) or nx, ny, nz = u0.shape at the top of a program
whose ARRAY SHAPES already name those symbols. _visit_assign (newast.py:3541, add_scalar at
:3703 with find_new_name=True) creates a transient that shadows the symbol the argument shapes
are declared with, and the parse dies later as KeyError: npwx / nx / nfrag / nproma / I.
Refusing the reassignment by name would be a fix; the KeyError is a crash.
5. or over symbol equalities: 'Equality' object is not iterable (4 kernels)
if dim == 0 or dim == -2: where dim is a symbol. visit_BoolOp (newast.py:5340) folds the
values pairwise through _visit_op; a sympy Equality then reaches code that iterates it.
TypeError: 'Equality' object is not iterable -- a crash, and it makes any symbolic branch guard
unusable.
6. np.where(cond, scalar, scalar) is refused (6 kernels)
ValueError: Both x and y cannot be scalars in numpy.where. numpy allows it; it is how a ported
GELU/clamp writes a two-way select.
7. The frontend HANGS parsing cloudsc (1 kernel)
hpcagent_bench/benchmarks/scientific_computing/structured_grids/cloudsc/cloudsc_dace.py does not
finish to_sdfg(simplify=False) in 180 s -- stuck, not slow. A kernel that hangs the parse is its
own finding: it also means any sweep that does not run one process per kernel reports a FLOOR.
8. Bitwise operators on a symbol raise NameError -- ONE bare eval (latent, 0 kernels today)
dace/frontend/python/replacements/utils.py:236:
pyval = eval(astutils.unparse(representative_value)) # no globals dict
silently borrows utils.py's module globals, which contain no symbolic head name. Reached whenever
a symbolic operand meets an array/scalar (operators.py:657-706).
Shifts survive the identical path only by accident: sym_type substitutes a representative value
first, and left_shift.eval / right_shift.eval (symbolic.py:1830-1860) FOLD when both args are
Numbers, collapsing to a literal. bitwise_and|or|xor|invert (symbolic.py:1812-1825) are
bodyless (class bitwise_and(DaceFunction): pass), so the string stays bitwise_and(1, 3) and
the bare eval raises. Giving that eval a real globals dict fixes every bitwise operator at once.
Frontend _pyop2symtype (replacements/operators.py:713-727) has exactly ONE binary entry,
"//" -> int_floor. Bitwise operators need entries, and they must map to the BARE classes:
patched to the __-prefixed variants, codegen emits __left_shift(N, 1) into C++
('__left_shift' was not declared in this scope) -- those are half-wired, produced by the parser
and printed by symstr but absent from symbolic.py:3299's binop map.
Two traps for whoever implements it:
- Cover BOTH directions --
1 << N fails as 'int' and 'symbol' where N << 1 works.
logical_left_shift / logical_right_shift (symbolic.py:1862-1895) are the Fortran ISHFT
zero-fill lowering. Python's >> on a negative int is ARITHMETIC. Picking the logical variant
from the python frontend is a silent miscompile, not a missing feature.
DaCe python-frontend issues found sweeping 576 autogenerated programs (2026-08-06)
Measured in
~/Work/optarenaagainst~/Work/dace@ecad3253f(extended). Every program isAUTOGENERATED from a numpy reference, so each of these is a construct the generator emits and the
kernel silently loses its DaCe column over. Filed, not fixed (this session's scope).
1. A runtime scalar is re-promoted to a FRESH symbol at every shape/slice use (64 kernels)
The single largest cause. A padded workspace is allocated from a scalar and then written through a
slice built from the SAME scalar:
The frontend promotes each USE of
paddingto its own__sym_<name>_<k>, so the write's extentbecomes
__sym_padded_slice - __sym_paddingand cannot be proven equal tox'sheight:Reusing one promoted symbol per scalar VERSION would make equal-by-construction extents provable.
Reachable via
dace/frontend/python/replacements/utils.py:169broadcast_together.2. A chained comparison raises a BODYLESS NotImplementedError (48 kernels)
dace/frontend/python/newast.py:5349:if 0 <= oy < oh:is the single commonest bounds test in ported conv/pool code. Two separatedefects: the feature is missing, AND the refusal names neither the construct nor the file. An
unsupported construct must name itself. (Worked around in the generator by splitting the chain,
but only where the middle operand is a Name/Constant -- the split evaluates it twice.)
3.
numpy.matmulhas no SDFG implementation registered (17 kernels)DaceSyntaxError: Function "numpy.matmul" is not registered with an SDFG implementation.np.dotand the
@operator work;np.matmuldoes not, which reads as an oversight rather than adecision.
4. Assigning a module-level
dc.symbolinside a program crashes with a bare KeyError (10 kernels)npwx, nvec, nvecx, npol = (int(npwx), ...)ornx, ny, nz = u0.shapeat the top of a programwhose ARRAY SHAPES already name those symbols.
_visit_assign(newast.py:3541,add_scalarat:3703withfind_new_name=True) creates a transient that shadows the symbol the argument shapesare declared with, and the parse dies later as
KeyError: npwx/nx/nfrag/nproma/I.Refusing the reassignment by name would be a fix; the KeyError is a crash.
5.
orover symbol equalities:'Equality' object is not iterable(4 kernels)if dim == 0 or dim == -2:wheredimis a symbol.visit_BoolOp(newast.py:5340) folds thevalues pairwise through
_visit_op; a sympyEqualitythen reaches code that iterates it.TypeError: 'Equality' object is not iterable-- a crash, and it makes any symbolic branch guardunusable.
6.
np.where(cond, scalar, scalar)is refused (6 kernels)ValueError: Both x and y cannot be scalars in numpy.where. numpy allows it; it is how a portedGELU/clamp writes a two-way select.
7. The frontend HANGS parsing cloudsc (1 kernel)
hpcagent_bench/benchmarks/scientific_computing/structured_grids/cloudsc/cloudsc_dace.pydoes notfinish
to_sdfg(simplify=False)in 180 s -- stuck, not slow. A kernel that hangs the parse is itsown finding: it also means any sweep that does not run one process per kernel reports a FLOOR.
8. Bitwise operators on a symbol raise NameError -- ONE bare
eval(latent, 0 kernels today)dace/frontend/python/replacements/utils.py:236:silently borrows
utils.py's module globals, which contain no symbolic head name. Reached whenevera symbolic operand meets an array/scalar (
operators.py:657-706).Shifts survive the identical path only by accident:
sym_typesubstitutes a representative valuefirst, and
left_shift.eval/right_shift.eval(symbolic.py:1830-1860) FOLD when both args areNumbers, collapsing to a literal.
bitwise_and|or|xor|invert(symbolic.py:1812-1825) arebodyless (
class bitwise_and(DaceFunction): pass), so the string staysbitwise_and(1, 3)andthe bare
evalraises. Giving thatevala real globals dict fixes every bitwise operator at once.Frontend
_pyop2symtype(replacements/operators.py:713-727) has exactly ONE binary entry,"//" -> int_floor. Bitwise operators need entries, and they must map to the BARE classes:patched to the
__-prefixed variants, codegen emits__left_shift(N, 1)into C++(
'__left_shift' was not declared in this scope) -- those are half-wired, produced by the parserand printed by
symstrbut absent fromsymbolic.py:3299's binop map.Two traps for whoever implements it:
1 << Nfails as'int' and 'symbol'whereN << 1works.logical_left_shift/logical_right_shift(symbolic.py:1862-1895) are the Fortran ISHFTzero-fill lowering. Python's
>>on a negative int is ARITHMETIC. Picking the logical variantfrom the python frontend is a silent miscompile, not a missing feature.