1818# pylint: disable=import-outside-toplevel, use-list-literal
1919# pylint: disable=no-value-for-parameter, unused-variable
2020# pylint: disable=unexpected-keyword-arg, unused-import, too-many-function-args
21- # ruff: noqa: RUF005, F821, F841
21+ # ruff: noqa: RUF005
22+ # F821: _qnn and _expr references are in unreachable code paths (guarded by NotImplementedError)
23+ # and will be resolved when quantization and vision op support are added.
24+ # ruff: noqa: F821
2225"""Tensorflow lite frontend."""
2326
2427import functools
@@ -468,7 +471,9 @@ def get_tensors(self, tensors_idx_list):
468471 qnn_params = dict ()
469472 qnn_params ["scale" ] = relax .const (scale , "float32" )
470473 qnn_params ["zero_point" ] = relax .const (zero_point , "int32" )
471- raise NotImplementedError ("Quantized operators not supported now" )
474+ raise NotImplementedError (
475+ "Quantized TFLite models are not yet supported in the Relax frontend"
476+ )
472477 return_list .append (TensorWrapper (tensor_idx , tensor , buffer , qnn_params ))
473478 return return_list
474479
@@ -530,20 +535,14 @@ def get_tensor_type_str(self, tensor_type):
530535 return "bool"
531536 raise NotImplementedError (f"Tensor type { tensor_type !s} is currently not supported" )
532537
533- def flatten_to_nd (self , x , x_shape , nd = 3 ):
538+ def flatten_to_nd (self , x , nd = 3 ):
534539 """Flatten input tensor to nd rank"""
535- ndims = self ._infer_shape (x_shape )[0 ]
540+ shape = x .struct_info .shape
541+ ndims = len (shape )
536542 if ndims == nd :
537543 return x
538- newshape = relax .op .concat (
539- [
540- relax .const ([- 1 ], dtype = self ._infer_type (x_shape ).checked_type .dtype ),
541- relax .op .strided_slice (x_shape , [ndims - nd + 1 ], [ndims ]),
542- ],
543- 0 ,
544- )
545- out = relax .op .reshape (x , self ._fold_constant (newshape ))
546- return out
544+ new_shape = [- 1 ] + [int (shape [i ]) for i in range (ndims - nd + 1 , ndims )]
545+ return relax .op .reshape (x , new_shape )
547546
548547 def has_same_qnn_params (self , lhs_tensor , rhs_tensor ):
549548 lhs_scale = lhs_tensor .qnn_params ["scale" ]
@@ -709,7 +708,7 @@ def _convert_resize(self, method, op):
709708
710709 # ResizeNearestNeighborOptions was added in tflite v1.13
711710 tflite_ver = 1120
712- if "ResizeNearestNeighborOptions" in dir ( tflite . BuiltinOptions ):
711+ if hasattr ( BuiltinOptions , "ResizeNearestNeighborOptions" ):
713712 tflite_ver = 1130
714713
715714 input_tensors = self .get_input_tensors (op )
@@ -947,8 +946,7 @@ def convert_shape(self, op):
947946 shape_options = ShapeOptions ()
948947 shape_options .Init (op_options .Bytes , op_options .Pos )
949948
950- out_type = self .get_tensor_type_str (shape_options .OutType ())
951- out = shape_of (self .get_tensor_expr (input_tensors [0 ]), dtype = out_type )
949+ out = relax .op .shape_of (self .get_tensor_expr (input_tensors [0 ]))
952950
953951 return out
954952
@@ -1428,6 +1426,7 @@ def convert_gather(self, op):
14281426
14291427 from tflite .BuiltinOptions import BuiltinOptions
14301428 from tflite .GatherOptions import GatherOptions
1429+ from tflite .TensorType import TensorType
14311430
14321431 input_tensors = self .get_input_tensors (op )
14331432 assert len (input_tensors ) == 2 , "input tensors length should be 2"
@@ -2804,115 +2803,66 @@ def convert_batch_matmul(self, op):
28042803
28052804 assert len (input_tensors ) == 2 , "two input tensor arguments expected"
28062805
2806+ if self .is_quantized (op ):
2807+ raise NotImplementedError (
2808+ "Quantized BATCH_MATMUL is not yet supported in the Relax frontend"
2809+ )
2810+
28072811 batch_matmul_options = BatchMatMulOptions ()
28082812 op_options = op .BuiltinOptions ()
28092813 batch_matmul_options .Init (op_options .Bytes , op_options .Pos )
28102814
28112815 input_a = self .get_expr (input_tensors [0 ].tensor_idx )
28122816 input_b = self .get_expr (input_tensors [1 ].tensor_idx )
28132817
2814- shape_a = shape_of (input_a )
2815- shape_b = shape_of (input_b )
2816- rank_a = self . _infer_shape (shape_a )[ 0 ]
2817- rank_b = self . _infer_shape (shape_b )[ 0 ]
2818+ shape_a = list (input_a . struct_info . shape )
2819+ shape_b = list (input_b . struct_info . shape )
2820+ rank_a = len (shape_a )
2821+ rank_b = len (shape_b )
28182822
28192823 if rank_a > 2 or rank_b > 2 :
2820- # Determine the output batch dimension
2821- new_a_shape = shape_a
2822- new_b_shape = shape_b
2823- if rank_a > rank_b :
2824- rank_diff = rank_a - rank_b
2825- new_b_shape = relax .op .concat (
2826- [
2827- relax .const (
2828- [1 ] * rank_diff , dtype = self ._infer_type (new_b_shape ).checked_type .dtype
2829- ),
2830- shape_b ,
2831- ],
2832- 0 ,
2833- )
2834- elif rank_a < rank_b :
2835- rank_diff = rank_b - rank_a
2836- new_a_shape = relax .op .concat (
2837- [
2838- relax .const (
2839- [1 ] * rank_diff , dtype = self ._infer_type (new_a_shape ).checked_type .dtype
2840- ),
2841- shape_a ,
2842- ],
2843- 0 ,
2844- )
2845- else :
2846- pass
2824+ # Broadcast batch dimensions
2825+ new_a_shape = [1 ] * max (0 , rank_b - rank_a ) + [int (s ) for s in shape_a ]
2826+ new_b_shape = [1 ] * max (0 , rank_a - rank_b ) + [int (s ) for s in shape_b ]
2827+ max_rank = max (rank_a , rank_b )
28472828
2848- out_batch = relax .op .concat (
2849- [
2850- relax .op .maximum (
2851- relax .op .strided_slice (new_b_shape , [i ], [i + 1 ]),
2852- relax .op .strided_slice (new_a_shape , [i ], [i + 1 ]),
2853- )
2854- for i in range (max (rank_a , rank_b ) - 2 )
2855- ],
2856- 0 ,
2857- )
2829+ batch_shape = [
2830+ max (new_a_shape [i ], new_b_shape [i ]) for i in range (max_rank - 2 )
2831+ ]
28582832
2859- a_broadcasted_shape = _fold_constant (
2860- _op .concat ([out_batch , _op .strided_slice (shape_a , [rank_a - 2 ], [rank_a ])], 0 )
2861- )
2862- b_broadcasted_shape = _fold_constant (
2863- _op .concat ([out_batch , _op .strided_slice (shape_b , [rank_b - 2 ], [rank_b ])], 0 )
2864- )
2865- if not tvm .ir .structural_equal (shape_a , a_broadcasted_shape ):
2866- input_a = relax .op .transform .broadcast_to (input_a , a_broadcasted_shape )
2867- if not tvm .ir .structural_equal (shape_b , b_broadcasted_shape ):
2868- input_b = relax .op .transform .broadcast_to (input_b , b_broadcasted_shape )
2833+ a_broadcast = batch_shape + [int (shape_a [- 2 ]), int (shape_a [- 1 ])]
2834+ b_broadcast = batch_shape + [int (shape_b [- 2 ]), int (shape_b [- 1 ])]
28692835
2870- input_a = self .flatten_to_nd (input_a , shape_a , 3 )
2871- input_b = self .flatten_to_nd (input_b , shape_b , 3 )
2836+ if [int (s ) for s in shape_a ] != a_broadcast :
2837+ input_a = relax .op .broadcast_to (input_a , a_broadcast )
2838+ if [int (s ) for s in shape_b ] != b_broadcast :
2839+ input_b = relax .op .broadcast_to (input_b , b_broadcast )
28722840
2873- if batch_matmul_options .AdjX ():
2841+ input_a = self .flatten_to_nd (input_a , 3 )
2842+ input_b = self .flatten_to_nd (input_b , 3 )
2843+
2844+ adj_x = batch_matmul_options .AdjX ()
2845+ adj_y = batch_matmul_options .AdjY ()
2846+
2847+ if adj_x :
28742848 input_a = relax .op .permute_dims (input_a , [0 , 2 , 1 ])
2875- if not batch_matmul_options . AdjY () :
2849+ if adj_y :
28762850 input_b = relax .op .permute_dims (input_b , [0 , 2 , 1 ])
28772851
2878- if self .is_quantized (op ):
2879- output = _qnn .op .batch_matmul (
2880- input_a ,
2881- input_b ,
2882- relax .const (0 , "int32" ),
2883- relax .const (0 , "int32" ),
2884- relax .const (1.0 , "float32" ),
2885- relax .const (1.0 , "float32" ),
2886- )
2887- else :
2888- output = relax .op .nn .batch_matmul (input_a , input_b )
2852+ output = relax .op .matmul (input_a , input_b )
28892853
2890- # Reshape output to original dimensions.
2891- output_shape = shape_of (output )
2854+ # Compute output matmul dims from original shapes
2855+ m_dim = int (shape_a [- 1 ]) if adj_x else int (shape_a [- 2 ])
2856+ n_dim = int (shape_b [- 2 ]) if adj_y else int (shape_b [- 1 ])
2857+ final_shape = [int (s ) for s in shape_a [: rank_a - 2 ]] + [m_dim , n_dim ]
2858+ return relax .op .reshape (output , final_shape )
28922859
2893- rank_out = self ._infer_shape (output_shape )[0 ]
2894-
2895- final_shape = relax .op .concat (
2896- [
2897- relax .op .strided_slice (shape_a , [0 ], [rank_a - 2 ]),
2898- relax .op .strided_slice (output_shape , [rank_out - 2 ], [rank_out ]),
2899- ],
2900- 0 ,
2901- )
2902-
2903- reshape = relax .op .reshape (output , self ._fold_constant (final_shape ))
2904- # qnn batch matmul returns a int32 tensor so we need to requantize
2905- if self .is_quantized (op ):
2906- return _qnn .op .requantize (
2907- reshape ,
2908- relax .const (1.0 , "float32" ),
2909- relax .const (0 , "int32" ),
2910- relax .const (1.0 , "float32" ),
2911- relax .const (0 , "int32" ),
2912- out_dtype = "int8" ,
2913- )
2914- else :
2915- return reshape
2860+ # rank <= 2: use matmul directly
2861+ if batch_matmul_options .AdjX ():
2862+ input_a = relax .op .permute_dims (input_a )
2863+ if batch_matmul_options .AdjY ():
2864+ input_b = relax .op .permute_dims (input_b )
2865+ return relax .op .matmul (input_a , input_b )
29162866
29172867 def convert_space_to_batch_nd (self , op ):
29182868 """space_to_batch_nd implementation."""
@@ -2974,6 +2924,7 @@ def convert_space_to_depth(self, op):
29742924
29752925 def convert_sparse_to_dense (self , op ):
29762926 """Convert TFLite SPARSE_TO_DENSE"""
2927+ from tflite .TensorType import TensorType
29772928
29782929 input_tensors = self .get_input_tensors (op )
29792930 assert len (input_tensors ) == 4 , "input tensors length should be 4"
@@ -3029,6 +2980,7 @@ def convert_transpose_conv(self, op):
30292980
30302981 from tflite .BuiltinOptions import BuiltinOptions
30312982 from tflite .Padding import Padding
2983+ from tflite .TensorType import TensorType
30322984 from tflite .TransposeConvOptions import TransposeConvOptions
30332985
30342986 input_tensors = self .get_input_tensors (op )
@@ -3226,6 +3178,7 @@ def convert_quantize(self, op):
32263178
32273179 def convert_dequantize (self , op ):
32283180 """Convert TFLite Dequantize"""
3181+ from tflite .TensorType import TensorType
32293182
32303183 input_tensors = self .get_input_tensors (op )
32313184 assert len (input_tensors ) == 1 , "input tensors length should be 1"
@@ -3251,6 +3204,11 @@ def convert_dequantize(self, op):
32513204
32523205 def convert_detection_postprocess (self , op ):
32533206 """Convert TFLite_Detection_PostProcess"""
3207+ raise NotImplementedError (
3208+ "DETECTION_POSTPROCESS requires vision ops (multibox_transform_loc, "
3209+ "non_max_suppression, get_valid_counts) not yet available in Relax. "
3210+ "See https://github.com/apache/tvm/issues/XXXX"
3211+ )
32543212 flexbuffer = op .CustomOptionsAsNumpy ().tobytes ()
32553213 custom_options = FlexBufferDecoder (flexbuffer ).decode ()
32563214
@@ -3381,6 +3339,11 @@ def convert_detection_postprocess(self, op):
33813339 def convert_nms_v5 (self , op ):
33823340 """Convert TFLite NonMaxSuppressionV5"""
33833341 # https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/non-max-suppression-v5
3342+ raise NotImplementedError (
3343+ "NON_MAX_SUPPRESSION_V5 requires vision ops (get_valid_counts, "
3344+ "non_max_suppression) not yet available in Relax. "
3345+ "See https://github.com/apache/tvm/issues/XXXX"
3346+ )
33843347
33853348 input_tensors = self .get_input_tensors (op )
33863349 assert len (input_tensors ) == 6 , "input tensor length should be 6"
@@ -3843,7 +3806,7 @@ def _def_prepare_dense_matrix_from_sparse(indices, level, prev_idx):
38433806
38443807def get_scalar_from_constant (expr ):
38453808 """Returns scalar value from Relax constant scalar."""
3846- assert isinstance (expr , _expr .Constant ) and not expr .data .shape , (
3809+ assert isinstance (expr , relax .Constant ) and not expr .data .shape , (
38473810 "Expr is not a constant scalar."
38483811 )
38493812 value = expr .data .numpy ()
@@ -4091,7 +4054,7 @@ def func(self, data):
40914054
40924055 with bb .function ("main" ):
40934056 input_list = []
4094- with bb .dataflow () as df : # pylint: disable=invalid-name, unused-variable
4057+ with bb .dataflow () as df : # noqa: F841 # pylint: disable=invalid-name, unused-variable
40954058 exp_tab = ExprTable ()
40964059 for model_input in model_inputs :
40974060 model_input_name = get_tensor_name (subgraph , model_input )
0 commit comments