gemini-code-assist[bot] commented on code in PR #19654:
URL: https://github.com/apache/tvm/pull/19654#discussion_r3333024356


##########
python/tvm/relax/frontend/tflite/tflite_frontend.py:
##########
@@ -755,6 +756,88 @@ def convert_hashtable_find(self, op):
             "HASHTABLE_FIND requires TensorType.STRING support in Relax TFLite 
frontend"
         )
 
+    def convert_hashtable_lookup(self, op):
+        """Convert TFLite HASHTABLE_LOOKUP for non-string value tensors."""
+        from tflite.TensorType import TensorType
+
+        input_tensors = self.get_input_tensors(op)
+        output_tensors = self.get_output_tensors(op)
+        if len(input_tensors) != 3 or len(output_tensors) != 2:
+            raise tvm.error.OpNotImplemented(
+                "HASHTABLE_LOOKUP expects lookup, key, and value inputs with 
two outputs"
+            )
+
+        lookup_tensor, key_tensor, value_tensor = input_tensors
+        output_tensor, hits_tensor = output_tensors
+
+        if (
+            lookup_tensor.tensor.Type() != TensorType.INT32
+            or key_tensor.tensor.Type() != TensorType.INT32
+        ):
+            raise tvm.error.OpNotImplemented(
+                "HASHTABLE_LOOKUP requires int32 lookup and key tensors"
+            )
+        if self._is_tflite_string_type(value_tensor.tensor.Type()):
+            raise tvm.error.OpNotImplemented(
+                "HASHTABLE_LOOKUP with TensorType.STRING values is not 
supported"
+            )
+        if value_tensor.tensor.Type() != output_tensor.tensor.Type():
+            raise tvm.error.OpNotImplemented(
+                "HASHTABLE_LOOKUP output dtype must match the value tensor 
dtype"
+            )
+        if hits_tensor.tensor.Type() != TensorType.UINT8:
+            raise tvm.error.OpNotImplemented("HASHTABLE_LOOKUP hits output 
must be uint8")
+
+        lookup_shape = to_int_list(self.get_tensor_shape(lookup_tensor))
+        key_shape = to_int_list(self.get_tensor_shape(key_tensor))
+        value_shape = to_int_list(self.get_tensor_shape(value_tensor))
+        output_shape = to_int_list(self.get_tensor_shape(output_tensor))
+        hits_shape = to_int_list(self.get_tensor_shape(hits_tensor))
+
+        if len(lookup_shape) != 1 or len(key_shape) != 1 or len(value_shape) < 
1:
+            raise tvm.error.OpNotImplemented(
+                "HASHTABLE_LOOKUP requires rank-1 lookup/key and rank>=1 value 
tensors"
+            )
+        if key_shape[0] != value_shape[0]:
+            raise tvm.error.OpNotImplemented(
+                "HASHTABLE_LOOKUP requires key and value tensors to agree on 
row count"
+            )
+        if key_shape[0] == 0:
+            raise tvm.error.OpNotImplemented(
+                "HASHTABLE_LOOKUP requires a non-empty key/value table"
+            )
+        if output_shape != [lookup_shape[0]] + value_shape[1:]:
+            raise tvm.error.OpNotImplemented(
+                "HASHTABLE_LOOKUP output shape must match lookup count and 
value tail shape"
+            )
+        if hits_shape != [lookup_shape[0]]:
+            raise tvm.error.OpNotImplemented(
+                "HASHTABLE_LOOKUP hits output shape must match lookup count"
+            )
+
+        lookup = self.get_tensor_expr(lookup_tensor)
+        key = self.get_tensor_expr(key_tensor)
+        value = self.get_tensor_expr(value_tensor)
+
+        positions = relax.op.bucketize(lookup, key, out_int32=True, 
right=False)
+        candidate_keys = relax.op.take(key, positions, axis=0, mode="clip")
+        in_range = relax.op.less(positions, relax.const(key_shape[0], "int32"))
+        found = relax.op.logical_and(in_range, relax.op.equal(candidate_keys, 
lookup))

Review Comment:
   ![high](https://www.gstatic.com/codereviewagent/high-priority.svg)
   
   The current implementation uses `relax.op.bucketize` to find the lookup 
positions. However, `bucketize` assumes that the boundaries (the `key` tensor) 
are sorted. In TFLite, `HASHTABLE_LOOKUP` keys are not guaranteed to be sorted. 
If the keys are unsorted, `bucketize` will perform an incorrect binary search 
and return wrong indices.
   
   To support unsorted keys correctly, we can use a broadcasted comparison to 
find the matching indices using `relax.op.equal`, `relax.op.argmax`, and 
`relax.op.max`.
   
   ```suggestion
           lookup_expanded = relax.op.expand_dims(lookup, axis=1)
           key_expanded = relax.op.expand_dims(key, axis=0)
           match_matrix = relax.op.equal(lookup_expanded, key_expanded)
           match_matrix_int = relax.op.astype(match_matrix, "int32")
   
           positions = relax.op.astype(relax.op.argmax(match_matrix_int, 
axis=1), "int32")
           found = relax.op.astype(relax.op.max(match_matrix_int, axis=1), 
"bool")
   ```



##########
tests/python/relax/test_frontend_tflite.py:
##########
@@ -5908,6 +5950,53 @@ def test_hashtable_size_uninitialized_unsupported():
         
_load_model_from_buffer(_build_tflite_hashtable_size_uninitialized_model())
 
 
+def test_hashtable_lookup_1d_value():
+    mod = 
_load_model_from_buffer(_build_tflite_hashtable_lookup_model(value_shape=[3]))
+
+    output, hits = _run_module(
+        mod,
+        np.array([1234, -292, -11, 0], dtype=np.int32),
+        np.array([-11, 0, 1234], dtype=np.int32),
+        np.array([0.0, 0.1, 0.4], dtype=np.float32),
+    )
+
+    np.testing.assert_allclose(output, np.array([0.4, 0.0, 0.0, 0.1], 
dtype=np.float32))
+    np.testing.assert_array_equal(hits, np.array([1, 0, 1, 1], dtype=np.uint8))

Review Comment:
   ![medium](https://www.gstatic.com/codereviewagent/medium-priority.svg)
   
   Since TFLite hashtable keys are not guaranteed to be sorted, we should 
update the test case to use unsorted keys to ensure correctness and prevent 
regressions.
   
   ```suggestion
       output, hits = _run_module(
           mod,
           np.array([1234, -292, -11, 0], dtype=np.int32),
           np.array([0, 1234, -11], dtype=np.int32),
           np.array([0.1, 0.4, 0.0], dtype=np.float32),
       )
   
       np.testing.assert_allclose(output, np.array([0.4, 0.0, 0.0, 0.1], 
dtype=np.float32))
       np.testing.assert_array_equal(hits, np.array([1, 0, 1, 1], 
dtype=np.uint8))
   ```



-- 
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