Aharrypotter commented on code in PR #19651:
URL: https://github.com/apache/tvm/pull/19651#discussion_r3333912058


##########
tests/python/relax/test_frontend_tflite.py:
##########
@@ -6926,6 +6928,240 @@ def 
test_stablehlo_options_missing_payload_unsupported():
         _load_model_from_buffer(buf)
 
 
+def _build_stablehlo_rng_model(algorithm, state_len, out_shape, 
out_tensor_type, const_state=None):
+    """Build a STABLEHLO_RNG_BIT_GENERATOR model.
+
+    When ``const_state`` is provided, the uint64 initial state is embedded as a
+    constant tensor (no graph input); otherwise it is a graph input.
+    """
+    builder = flatbuffers.Builder(1024)
+
+    _tfl_stablehlo_rng_opts.StablehloRngBitGeneratorOptionsStart(builder)
+    
_tfl_stablehlo_rng_opts.StablehloRngBitGeneratorOptionsAddAlgorithm(builder, 
algorithm)
+    rng_opts = 
_tfl_stablehlo_rng_opts.StablehloRngBitGeneratorOptionsEnd(builder)
+
+    rng_builtin = 
_get_stablehlo_builtin_operator("STABLEHLO_RNG_BIT_GENERATOR")
+    rng_code = _build_operator_code(builder, rng_builtin)
+
+    main_tensors = [
+        _build_tensor(builder, 0, [state_len], 
tensor_type=_tfl_tensor_type.UINT64),
+        _build_tensor(builder, 1, [state_len], 
tensor_type=_tfl_tensor_type.UINT64),
+        _build_tensor(builder, 2, list(out_shape), 
tensor_type=out_tensor_type),
+    ]
+    rng_op = _build_operator(
+        builder,
+        0,
+        [0],
+        [1, 2],
+        
builtin_options2_type=_tfl_builtin_options2.StablehloRngBitGeneratorOptions,
+        builtin_options2=rng_opts,
+    )
+    main_subgraph = _build_subgraph(
+        builder,
+        tensors=main_tensors,
+        operators=[rng_op],
+        inputs=[] if const_state is not None else [0],
+        outputs=[1, 2],
+    )
+
+    state_data = None
+    if const_state is not None:
+        state_data = np.array(const_state, dtype="uint64").tobytes()
+    buffers = [
+        _build_buffer(builder, data=state_data),
+        _build_buffer(builder),
+        _build_buffer(builder),
+    ]
+    return _finish_tflite_model(
+        builder,
+        subgraph=main_subgraph,
+        operator_codes=[rng_code],
+        buffers=buffers,
+    )
+
+
+def _run_stablehlo_rng_model(algorithm, state_len, out_shape, out_tensor_type, 
init_state):
+    """Import, compile, and execute an RNG model, returning (output_state, 
output)."""
+    buf = _build_stablehlo_rng_model(algorithm, state_len, out_shape, 
out_tensor_type)
+    mod = _load_model_from_buffer(buf)
+    ex = tvm.compile(mod, tvm.target.Target("llvm"))
+    vm = relax.VirtualMachine(ex, tvm.cpu())
+    result = vm["main"](tvm.runtime.tensor(np.array(init_state, 
dtype="uint64")))
+    return result[0].numpy(), result[1].numpy()
+
+
+# Expected vectors are taken verbatim from the TFLite runtime kernel test
+# (tensorflow/lite/kernels/rng_bit_generator_test.cc), guaranteeing bit-exact 
parity.
+_RNG_THREEFRY_EXPECTED = {
+    "int32": [43444564, -2144348869, -315321645, -549236733, 1672743891, 
-54463903],
+    "uint32": [43444564, 2150618427, 3979645651, 3745730563, 1672743891, 
4240503393],
+    "int64": [
+        -9209908263526143660,
+        -2358953802017238317,
+        -233920680524772397,
+        2658481902456610144,
+        -2022031683723149139,
+        -2324041912354448873,
+    ],
+    "uint64": [
+        9236835810183407956,
+        16087790271692313299,
+        18212823393184779219,
+        2658481902456610144,
+        16424712389986402477,
+        16122702161355102743,
+    ],
+}
+_RNG_THREEFRY_STATE = {"int32": [1, 5], "uint32": [1, 5], "int64": [1, 8], 
"uint64": [1, 8]}
+_RNG_PHILOX_EXPECTED = {
+    "int32": [-263854262, 1366700262, 495645701, -1243243882, 89414891, 
1917262711],
+    "uint32": [4031113034, 1366700262, 495645701, 3051723414, 89414891, 
1917262711],
+    "int64": [
+        5869932932755744586,
+        -5339691813646437371,
+        8234580641674714347,
+        2641225993340350124,
+        1962472297844690804,
+        -3580856229565614135,
+    ],
+    "uint64": [
+        5869932932755744586,
+        13107052260063114245,
+        8234580641674714347,
+        2641225993340350124,
+        1962472297844690804,
+        14865887844143937481,
+    ],
+}
+_RNG_PHILOX_STATE = {
+    "int32": [1, 4, 3],
+    "uint32": [1, 4, 3],
+    "int64": [1, 5, 3],
+    "uint64": [1, 5, 3],
+}
+
+
[email protected](
+    "out_dtype,out_tensor_type",
+    [
+        ("int32", _tfl_tensor_type.INT32),
+        ("uint32", _tfl_tensor_type.UINT32),
+        ("int64", _tfl_tensor_type.INT64),
+        ("uint64", _tfl_tensor_type.UINT64),
+    ],
+)
+def test_stablehlo_rng_bit_generator_threefry(out_dtype, out_tensor_type):
+    """TFLite STABLEHLO_RNG_BIT_GENERATOR THREEFRY matches the runtime kernel 
bit-exactly."""
+    state, output = _run_stablehlo_rng_model(
+        _tfl_rng_algorithm.THREEFRY, 2, [2, 3], out_tensor_type, [1, 2]
+    )
+    assert output.flatten().tolist() == _RNG_THREEFRY_EXPECTED[out_dtype]
+    assert state.tolist() == _RNG_THREEFRY_STATE[out_dtype]
+
+
[email protected](
+    "out_dtype,out_tensor_type",
+    [
+        ("int32", _tfl_tensor_type.INT32),
+        ("uint32", _tfl_tensor_type.UINT32),
+        ("int64", _tfl_tensor_type.INT64),
+        ("uint64", _tfl_tensor_type.UINT64),
+    ],
+)
+def test_stablehlo_rng_bit_generator_philox(out_dtype, out_tensor_type):
+    """TFLite STABLEHLO_RNG_BIT_GENERATOR PHILOX matches the runtime kernel 
bit-exactly."""
+    state, output = _run_stablehlo_rng_model(
+        _tfl_rng_algorithm.PHILOX, 3, [2, 3], out_tensor_type, [1, 2, 3]
+    )
+    assert output.flatten().tolist() == _RNG_PHILOX_EXPECTED[out_dtype]
+    assert state.tolist() == _RNG_PHILOX_STATE[out_dtype]
+
+
+def test_stablehlo_rng_bit_generator_default_matches_philox():
+    """TFLite STABLEHLO_RNG_BIT_GENERATOR DEFAULT resolves to the PHILOX 
algorithm."""
+    state, output = _run_stablehlo_rng_model(
+        _tfl_rng_algorithm.DEFAULT, 3, [2, 3], _tfl_tensor_type.INT32, [1, 2, 
3]
+    )
+    assert output.flatten().tolist() == _RNG_PHILOX_EXPECTED["int32"]
+    assert state.tolist() == _RNG_PHILOX_STATE["int32"]
+
+
+def test_stablehlo_rng_bit_generator_deterministic():
+    """Re-running the imported RNG kernel yields identical bit-exact output."""
+    buf = _build_stablehlo_rng_model(_tfl_rng_algorithm.PHILOX, 3, [3, 3], 
_tfl_tensor_type.INT32)
+    mod = _load_model_from_buffer(buf)
+    ex = tvm.compile(mod, tvm.target.Target("llvm"))
+    vm = relax.VirtualMachine(ex, tvm.cpu())
+    init = tvm.runtime.tensor(np.array([7, 8, 9], dtype="uint64"))
+    first = vm["main"](init)
+    second = vm["main"](init)
+    np.testing.assert_equal(first[1].numpy(), second[1].numpy())
+    np.testing.assert_equal(first[0].numpy(), second[0].numpy())
+
+
+def test_stablehlo_rng_bit_generator_constant_state():
+    """A constant uint64 initial state imports and stays bit-exact (no graph 
input)."""
+    buf = _build_stablehlo_rng_model(
+        _tfl_rng_algorithm.THREEFRY, 2, [2, 3], _tfl_tensor_type.INT32, 
const_state=[1, 2]
+    )
+    mod = _load_model_from_buffer(buf)
+    assert len(mod["main"].params) == 0
+    ex = tvm.compile(mod, tvm.target.Target("llvm"))
+    vm = relax.VirtualMachine(ex, tvm.cpu())
+    result = vm["main"]()
+    assert result[1].numpy().flatten().tolist() == 
_RNG_THREEFRY_EXPECTED["int32"]
+    assert result[0].numpy().tolist() == _RNG_THREEFRY_STATE["int32"]

Review Comment:
   I think the `compile + VirtualMachine` calls in these RNG tests are 
intentional, though they cover slightly different things.
   
   `STABLEHLO_RNG_BIT_GENERATOR` is different from the other StableHLO TFLite 
ops in this file: the importer generates a custom TIR implementation of the 
TFLite runtime RNG algorithm, rather than lowering to an existing Relax op. A 
structural-equality test would only prove that the generated module has the 
expected shape; it would not catch mistakes in the Threefry/Philox rounds, 
counter advancement, word packing, or signed/unsigned reinterpretation.
   
   The nightly E2E path in `_verify_model` also does not cover this case. These 
tests build a raw TFLite flatbuffer directly, so there is no TensorFlow 
callable/reference path for nightly CI to run and compare. The expected vectors 
below are copied from the TFLite runtime kernel tests, so compiling and 
executing the imported module here is what actually checks bit-exact parity 
with TFLite.



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