Skip to content

[Relax][Frontend][TFLite] Support dynamic RANGE scalar bounds#19867

Open
Aharrypotter wants to merge 1 commit into
apache:mainfrom
Aharrypotter:tflite-range-dynamic-19412
Open

[Relax][Frontend][TFLite] Support dynamic RANGE scalar bounds#19867
Aharrypotter wants to merge 1 commit into
apache:mainfrom
Aharrypotter:tflite-range-dynamic-19412

Conversation

@Aharrypotter

Copy link
Copy Markdown
Contributor

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:

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:

ruff format --check: 2 files already formatted
ruff check: All checks passed
range tests: 12 passed, 536 deselected
full TFLite pytest: 548 passed

References

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for TFLite RANGE operators with dynamic (runtime) scalar bounds in the Relax frontend, handling both integer and float data types by computing the element count in-graph and lifting it to a symbolic dimension. It also replaces the previous unsupported-feature test with comprehensive parameterized tests. Feedback on the changes points out a critical typo where tirx is used instead of tir in the _scalar_tensor_to_dim helper, which will cause a NameError at runtime.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread python/tvm/relax/frontend/tflite/tflite_frontend.py
@Aharrypotter Aharrypotter force-pushed the tflite-range-dynamic-19412 branch from a359f03 to c9bd9ca Compare June 23, 2026 03:42
Previously convert_range raised OpNotImplemented when start/limit/delta
were runtime (non-constant) scalar tensors, so any TFLite model that
computes RANGE bounds at runtime failed to import. This was one of the
"partial implementation" items tracked in apache#19412.

relax.op.arange only takes compile-time PrimExpr bounds, and its struct-info
length formula (InferTypeArange) has no negative-step branch, so feeding
symbolic bounds straight in would mis-declare descending ranges. Instead,
compute the element count in-graph and lift it to one symbolic output
dimension via relax.op.tensor_to_shape + match_cast (the bridge already used
by _get_shape_expr_from_tensor), so the declared and runtime lengths match by
construction. Values are rebuilt as arange(0, count) * delta + start. One
unified path covers both dtypes:

- int: count = -floor_divide(start - limit, delta), exact and sign-agnostic
  (no float-precision loss), equal to ceil((limit - start) / delta);
- float: count = ceil((limit - start) / delta).

No new Relax op is needed.

Replace the "not supported" test with a compile-and-run test covering
ascending/descending integer and float dynamic bounds.
@Aharrypotter Aharrypotter force-pushed the tflite-range-dynamic-19412 branch from c9bd9ca to 8dfc70e Compare June 23, 2026 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant