Aharrypotter opened a new pull request, #19867:
URL: https://github.com/apache/tvm/pull/19867
## Summary
This PR adds Relax TFLite frontend support for dynamic (runtime) scalar
bounds
in the `RANGE` operator, addressing the `RANGE` "fix partial implementations"
item from #19412 section C.
`convert_range` previously lowered only **constant** `start`, `limit`, and
`delta` to `relax.op.arange` and raised `OpNotImplemented` for runtime scalar
bounds (the guard added in #19401). Models that compute RANGE bounds at
runtime
could therefore not be imported. This PR makes the dynamic path work for both
integer and float bounds, ascending or descending, without adding a new Relax
op. The change is limited to the `RANGE` converter and its test.
#19813 added a batch of missing TFLite operator mappings but did not touch
this
partial-implementation item; this PR closes it.
## Design
### Dynamic scalar bounds via count-lift
`relax.op.arange` only accepts compile-time `PrimExpr` bounds. The frontend
already has a runtime-scalar -> symbolic-dimension bridge
(`relax.op.tensor_to_shape` + `match_cast`, as used by
`_get_shape_expr_from_tensor`), so no new op is needed.
Rather than feed symbolic bounds straight into `arange`, the converter
computes
the element **count** in-graph and lifts that single value to one symbolic
output dimension `L`, then rebuilds the values as `arange(0, L) * delta +
start`.
Lifting the count (instead of the bounds) keeps the declared and runtime
output
lengths equal by construction: `arange`'s struct-info length formula
(`InferTypeArange`) has no negative-step branch, so feeding symbolic bounds
directly would mis-declare descending ranges relative to the TOPI runtime
length.
The count is `max(0, ceil((limit - start) / delta))`, computed per dtype:
- **integer**: `-floor_divide(start - limit, delta)` — exact, sign-agnostic,
and
free of float-precision loss; equal to `ceil((limit - start) / delta)`.
- **float**: `ceil((limit - start) / delta)`.
Constant (all-bounds-constant) RANGE keeps the existing direct-`arange` path
unchanged.
## Operator Support
| Operator | TFLite inputs | Relax lowering | Supported subset |
|---|---|---|---|
| `RANGE` | scalar `start`, `limit`, `delta` | `relax.op.arange` (constant
bounds); count-lift + `arange(0, L) * delta + start` (dynamic bounds) | int and
float, constant or runtime scalar bounds, ascending or descending |
## Tests
The dynamic test compiles the imported module and runs it on the Relax VM,
comparing the output against `numpy.arange`. The constant-bound structural
test
is unchanged.
| Test | Coverage |
|---|---|
| `test_range` | constant scalar bounds (existing, unchanged) |
| `test_range_dynamic_scalar_inputs` | runtime scalar bounds: int and float,
ascending and descending |
Local validation:
```bash
python -m ruff format --check \
python/tvm/relax/frontend/tflite/tflite_frontend.py \
tests/python/relax/test_frontend_tflite.py
python -m ruff check \
python/tvm/relax/frontend/tflite/tflite_frontend.py \
tests/python/relax/test_frontend_tflite.py
python -m pytest \
tests/python/relax/test_frontend_tflite.py -k range -q
python -m pytest \
tests/python/relax/test_frontend_tflite.py -q
```
Result:
```text
ruff format --check: 2 files already formatted
ruff check: All checks passed
range tests: 12 passed, 536 deselected
full TFLite pytest: 548 passed
```
## References
- Issue #19412 section C: fix partial TFLite operator implementations
(`RANGE`)
- PR #19401: added the `RANGE` dynamic-scalar guard and its test
- PR #18868: introduced the Relax TFLite frontend and `convert_range`
--
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]