hiyufan opened a new pull request, #20245:
URL: https://github.com/apache/tvm/pull/20245

   Fixes: #20227
   
   ## Summary
   
   `tvm.relax.frontend.torch.from_fx` crashed with an internal
   `TypeError: reduce() of empty iterable with no initial value` when a traced 
model
   contained a `flatten` whose `start_dim` comes after its `end_dim`.
   
   torch rejects such a `flatten` with a clear `RuntimeError`, but only when 
the model is
   executed. `fx.symbolic_trace` does not execute the model, so the invalid 
node reaches
   the frontend as a perfectly traceable graph and has to be rejected there. 
This PR
   validates the dims in `_flatten_impl`, mirroring what `from_onnx` now does 
for
   `Flatten` (#20145).
   
   Only the `from_fx` / `TorchFXImporter` path is affected. 
`from_exported_program` runs
   `run_decompositions()` by default, which lowers `aten.flatten.using_ints` to
   `aten.view`, so `_flatten_impl` is never reached there.
   
   ## Root cause
   
   `_flatten_impl` in 
`python/tvm/relax/frontend/torch/base_fx_graph_translator.py`
   normalized negative dims but never checked their range or their ordering:
   
   ```python
   start_dim = start_dim if start_dim >= 0 else len(shape) + start_dim
   end_dim = end_dim if end_dim >= 0 else len(shape) + end_dim
   flattened = reduce(lambda x, y: x * y, [shape[i] for i in range(start_dim, 
end_dim + 1)])
   ```
   
   For `start_dim=2, end_dim=1` the `range` is empty, so `functools.reduce` is 
called over
   an empty iterable with no initial value. Out-of-range dims (`flatten(x, 0, 
3)` on a
   rank-3 input) leaked an `IndexError` out of `shape[i]` instead.
   
   Both entry points that reach this helper are affected: `torch.flatten` 
(dispatched via
   `_flatten`) and `torch.nn.Flatten` (via `_flatten_module`).
   
   ## Fix
   
   Normalize both dims against `max(rank, 1)`, validate each against `[-r, 
r-1]`, and
   reject `start_dim > end_dim` with torch's own wording:
   
   ```python
   dim_post_expr = max(rank, 1)
   norm_start_dim = start_dim + dim_post_expr if start_dim < 0 else start_dim
   norm_end_dim = end_dim + dim_post_expr if end_dim < 0 else end_dim
   ...
   if norm_start_dim > norm_end_dim:
       raise ValueError("flatten() has invalid args: start_dim cannot come 
after end_dim")
   ```
   
   A 0-d input is handled explicitly: torch normalizes flatten dims against a 
rank of at
   least one, so `torch.flatten(scalar)` is valid and returns a 1-d tensor 
holding the
   single element. The old code hit the same empty-`reduce` crash for that 
input.
   
   ## Validation
   
   Built from source (CPU-only, `USE_LLVM=OFF`) on Linux, torch 2.13.0+cpu, 
Python 3.14.
   
   Behavior on the reported cases, measured before and after the change:
   
   | Input shape | dims | Before | After |
   |---|---|---|---|
   | `(2,3,4)` | `(2,1)` | `TypeError: reduce() of empty iterable with no 
initial value` | `ValueError: flatten() has invalid args: start_dim cannot come 
after end_dim` |
   | `(2,3,4)` | `(0,3)` | `IndexError: ShapeExpr index out of range` | 
`ValueError: flatten end_dim 3 is out of range [-3, 2] for an input of rank 3` |
   | `(2,3,4)` | `(-4,2)` | `ValueError: Reshape expects the new shape to be 
convertible from the old shape` | `ValueError: flatten start_dim -4 is out of 
range [-3, 2] for an input of rank 3` |
   | `()` (0-d) | `(0,-1)` | `TypeError: reduce() of empty iterable with no 
initial value` | converts, output `(1,)` |
   
   The `(-4,2)` row is worth calling out: an out-of-range negative `start_dim` 
did not fail in
   `_flatten_impl` at all. It normalized to `-1`, so `range(-1, 3)` silently 
folded the last
   dimension into the product and emitted a `reshape` to `(96,)` for a 
24-element tensor, which
   only failed later inside `relax.op.reshape`. That is a mis-computation, not 
just a crash.
   
   Valid dims are unaffected — `(1,3,10,10)` with `(2,-1)`, and `(2,3,4)` with 
`(1,2)`, `(0,-1)`,
   `(-3,-1)` and `(2,2)`, all convert to the same shapes as before.
   
   Regression run of the whole `from_fx` suite, base commit vs. this branch:
   
   | | base (`HEAD~1`) | this branch |
   |---|---|---|
   | passed | 164 | 166 (+2 new tests) |
   | failed | 16 | 16 |
   | skipped | 1 | 1 |
   
   The failure sets are identical line for line — the 16 failures 
(`test_dtypes[*]`, `test_round`)
   are pre-existing on the base commit in this environment and unrelated to 
this change.
   
   Tests added to `tests/python/relax/test_frontend_from_fx.py`:
   
   - `test_flatten_invalid_dims` — `start_dim > end_dim` through both 
`torch.flatten` and
     `torch.nn.Flatten`, plus an out-of-range `end_dim`
   - `test_flatten_scalar_input` — 0-d input flattens to shape `(1,)`
   
   ```
   $ pytest tests/python/relax/test_frontend_from_fx.py -k flatten -v
   test_flatten PASSED
   test_flatten_invalid_dims PASSED
   test_flatten_scalar_input PASSED
   3 passed, 180 deselected
   
   $ pre-commit run --files 
python/tvm/relax/frontend/torch/base_fx_graph_translator.py \
                       tests/python/relax/test_frontend_from_fx.py
   ruff check ....... Passed
   ruff format ...... Passed
   (all hooks passed)
   ```
   
   ## Files changed
   
   - `python/tvm/relax/frontend/torch/base_fx_graph_translator.py` — 
`_flatten_impl`:
     normalize and validate dims, handle a 0-d input.
   - `tests/python/relax/test_frontend_from_fx.py` — regression tests.
   
   ## Note for reviewers
   
   The 0-d handling (`dim_post_expr = max(rank, 1)` plus the `rank == 0` 
branch) is
   separable from the reported bug. It fixes the same empty-`reduce` crash for 
scalar
   inputs and keeps the new range check from rejecting a valid 
`torch.flatten(scalar)`,
   but if you would rather keep this PR to exactly the reported case I am happy 
to drop
   that hunk and its test.
   
   ---
   
   This change was developed with AI assistance. The build, the before/after 
comparison
   and the test runs reported above were all executed locally against this 
branch as
   submitted.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to