Skip to content

Document that roll leaves a dynamic zero length dimension undefined - #3030

Open
om singhal (Om-singhaI) wants to merge 1 commit into
microsoft:mainfrom
Om-singhaI:fix/roll-dynamic-zero-dim
Open

Document that roll leaves a dynamic zero length dimension undefined#3030
om singhal (Om-singhaI) wants to merge 1 commit into
microsoft:mainfrom
Om-singhaI:fix/roll-dynamic-zero-dim

Conversation

@Om-singhaI

@Om-singhaI om singhal (Om-singhaI) commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

torch.roll exported with a dimension that can be zero still reaches Mod with a
zero divisor when that dimension is zero at run time. The 0 in self.shape early
return added in #3024 only covers a length known to be zero at export time, and ONNX
leaves modulo by zero undefined.

Undefined does not always mean loud, which is why it is worth writing down. On
onnxruntime 1.23.2 for macOS arm64 the zero divisor raises nothing and returns the
dividend, which Slice clamps back into range so the result still looks correct; on the build in
#3024 it fails with Integer modulo by zero.

This is a comment only change. The behavior is deliberately unchanged and the two
asserts stay as they are; each now carries the limitation next to it, and the early
return in aten_roll no longer reads as though it covers the dynamic case.

@justinchuby

Copy link
Copy Markdown
Collaborator

Hopefully this doesn't over complicate the implementation? It is also possible to assume input will never be zero sized, I hope?

@Om-singhaI

Copy link
Copy Markdown
Contributor Author

It is small: one Max on the divisor in each of the two helpers, and it let me drop the two asserts that used to claim a zero could never reach them, so the net is close to even.

On assuming nonzero, I would rather not. torch.roll on an empty tensor is legal and returns it unchanged, and a dim declared min=0 is a shape the exporter was told to expect. What makes it awkward is that Mod by zero is undefined rather than just unusual, so the same model throws on your build and quietly returns a clamped result on mine.

It is a rare shape though. If you would rather not carry the guard at all I am happy to close this. One middle option if the cost is what bothers you: only emit the Max when the length is not a static int, since a static one has already returned Identity for a zero upstream. Every static export then comes out byte identical to today and the guard appears only where it cannot be proven unnecessary.

@justinchuby

Copy link
Copy Markdown
Collaborator

Thanks. I suggest we note the limitation clearly in the source, and then keep the status quo until someone actually needs the behavior.

For onnxruntime, it would be great if you can update the different paths to all produce an error.

…undefined

I dropped the Max guard on the modulo divisor and left the behavior as it is.
The limitation is now written at both roll helpers: the export time check does
not cover a dynamic dimension that is zero at run time, ONNX leaves that modulo
undefined, and onnxruntime differs by build.
@Om-singhaI om singhal (Om-singhaI) changed the title Guard the roll modulo against a dynamic zero length dimension Document that roll leaves a dynamic zero length dimension undefined Sep 5, 2026
@Om-singhaI

Copy link
Copy Markdown
Contributor Author

Dropped the Max guard and left the behavior exactly as it is, so the graph is unchanged from main. The limitation is now written at both helpers next to the asserts from #3024: the check only covers a length known to be zero at export time, a dynamic dimension that is zero at run time still divides by zero there, and onnxruntime throws on some builds and quietly returns the dividend on others. I will look at the onnxruntime paths separately, though I would rather not put a date on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

2 participants