Support transparent id in composite id (#12238)
Co-authored-by: Geoffrey Martin-Noble <gcmn@google.com>
diff --git a/build_tools/python/e2e_test_framework/CMakeLists.txt b/build_tools/python/e2e_test_framework/CMakeLists.txt
index 62541ac..2abba74 100644
--- a/build_tools/python/e2e_test_framework/CMakeLists.txt
+++ b/build_tools/python/e2e_test_framework/CMakeLists.txt
@@ -11,4 +11,11 @@
"serialization_test.py"
)
+iree_build_tools_py_test(
+ NAME
+ unique_ids_test
+ SRC
+ "unique_ids_test.py"
+)
+
add_subdirectory(device_specs)
diff --git a/build_tools/python/e2e_test_framework/definitions/iree_definitions.py b/build_tools/python/e2e_test_framework/definitions/iree_definitions.py
index 52b933b..b16c921 100644
--- a/build_tools/python/e2e_test_framework/definitions/iree_definitions.py
+++ b/build_tools/python/e2e_test_framework/definitions/iree_definitions.py
@@ -15,22 +15,6 @@
from e2e_test_framework import serialization, unique_ids
-def _hash_composite_id(keys: Sequence[str]) -> str:
- """Computes the composite hash id from string keys.
-
- String keys are the component ids that compose this composite object. We hash
- the composite id since the id isn't designed to be inspected and insufficient
- to reconstruct the original composite object.
-
- Args:
- keys: list of string keys.
-
- Returns:
- Unique hash id.
- """
- return hashlib.sha256(":".join(keys).encode("utf-8")).hexdigest()
-
-
class TargetBackend(Enum):
"""IREE target backend."""
LLVM_CPU = "llvm-cpu"
@@ -185,7 +169,7 @@
import_config: ImportConfig
def composite_id(self):
- return _hash_composite_id([self.model.id, self.import_config.id])
+ return unique_ids.hash_composite_id([self.model.id, self.import_config.id])
@staticmethod
def from_model(model: common_definitions.Model):
@@ -204,7 +188,7 @@
compile_config: CompileConfig
def composite_id(self):
- return _hash_composite_id(
+ return unique_ids.hash_composite_id(
[self.imported_model.composite_id(), self.compile_config.id])
@@ -218,7 +202,7 @@
input_data: common_definitions.ModelInputData
def composite_id(self):
- return _hash_composite_id([
+ return unique_ids.hash_composite_id([
self.module_generation_config.composite_id(),
self.module_execution_config.id, self.target_device_spec.id,
self.input_data.id
diff --git a/build_tools/python/e2e_test_framework/unique_ids.py b/build_tools/python/e2e_test_framework/unique_ids.py
index e26ea00..b9f80c9 100644
--- a/build_tools/python/e2e_test_framework/unique_ids.py
+++ b/build_tools/python/e2e_test_framework/unique_ids.py
@@ -3,11 +3,63 @@
# Licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-"""List of unique random IDs in the benchmark suites.
+"""List of unique random IDs in the framework and id utilities.
Each ID should be generated from uuid.uuid4().
"""
+import hashlib
+from typing import Sequence
+
+# Special id which will be ignored when calculating the composite id.
+#
+# It should only be used when adding a new field to a composite object while
+# we want to maintain the same id on the existing composite objects.
+#
+# In such case, you need to create a "default config" for the new field with
+# this id and populate that config to the fields of the existing objects. The
+# composite id computing function will ignore this id and keep the output
+# unchanged.
+TRANSPARENT_ID = "00000000-0000-0000-0000-000000000000"
+
+
+def hash_composite_id(keys: Sequence[str]) -> str:
+ """Computes the composite hash id from string keys.
+
+ String keys are the component ids that compose this composite object. We hash
+ the composite id since the id isn't designed to be inspected and insufficient
+ to reconstruct the original composite object.
+
+ Note that the output is sensitive to the order of the keys, and any key ==
+ TRANSPARENT_ID will be skipped. When adding a new key to the keys, the new key
+ should be always appended to the end. In this way, the composite id can be
+ unchanged for the existing composite object if they use TRANSPARENT_ID on the
+ new keyed field.
+
+ The composite id is computed in the following steps:
+ 1. Index each key with its position in the list from 0.
+ 2. Remove any key == TRANSPARENT_ID
+ 3. Get the SHA256 hex digest of "0-key_0:1-key_1:..."
+
+ Step 1 is needed to avoid the ambiguity between:
+ ["key_abc", TRANSPARENT_ID] and [TRANSPARENT_ID, "key_abc"]
+ since after removing TRANSPARENT_ID, they both become ["key_abc"] without the
+ position index.
+
+ Args:
+ keys: list of string keys.
+
+ Returns:
+ Unique composite id.
+ """
+ trimmed_indexed_key = [
+ f"{index}-{key}" for index, key in enumerate(keys)
+ if key != TRANSPARENT_ID
+ ]
+ return hashlib.sha256(
+ ":".join(trimmed_indexed_key).encode("utf-8")).hexdigest()
+
+
# Models
MODEL_DEEPLABV3_FP32 = "c36c63b0-220a-4d78-8ade-c45ce47d89d3"
MODEL_MOBILESSD_FP32 = "0e466f69-91d6-4e50-b62b-a82b6213a231"
diff --git a/build_tools/python/e2e_test_framework/unique_ids_test.py b/build_tools/python/e2e_test_framework/unique_ids_test.py
new file mode 100644
index 0000000..2fdf8fc
--- /dev/null
+++ b/build_tools/python/e2e_test_framework/unique_ids_test.py
@@ -0,0 +1,52 @@
+# Copyright 2023 The IREE Authors
+#
+# Licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+import hashlib
+import unittest
+
+import unique_ids
+
+
+class UniqueIdsTest(unittest.TestCase):
+
+ def test_hash_composite_id(self):
+ output = unique_ids.hash_composite_id(["abc", "123"])
+
+ self.assertEquals(
+ output,
+ hashlib.sha256(f"0-abc:1-123".encode("utf-8")).hexdigest())
+
+ def test_hash_composite_id_diff_keys(self):
+ ids = [
+ unique_ids.hash_composite_id([]),
+ unique_ids.hash_composite_id(["abc", "123"]),
+ unique_ids.hash_composite_id(["123", "abc"]),
+ unique_ids.hash_composite_id(["123", unique_ids.TRANSPARENT_ID]),
+ unique_ids.hash_composite_id(["123", "abc", "xyz"]),
+ unique_ids.hash_composite_id(["123", unique_ids.TRANSPARENT_ID, "xyz"])
+ ]
+
+ # Check if they are all distinct.
+ self.assertCountEqual(set(ids), ids)
+
+ def test_hash_composite_id_unchanged_with_transparent_id(self):
+ existing_id = unique_ids.hash_composite_id(["abc"])
+ new_id_a = unique_ids.hash_composite_id(["abc", unique_ids.TRANSPARENT_ID])
+ new_id_b = unique_ids.hash_composite_id(
+ ["abc", unique_ids.TRANSPARENT_ID, unique_ids.TRANSPARENT_ID])
+
+ self.assertEquals(existing_id, new_id_a)
+ self.assertEquals(existing_id, new_id_b)
+
+ def test_hash_composite_id_with_transparent_ids_in_diff_pos(self):
+ id_a = unique_ids.hash_composite_id([unique_ids.TRANSPARENT_ID, "abc"])
+ id_b = unique_ids.hash_composite_id(["abc", unique_ids.TRANSPARENT_ID])
+
+ self.assertNotEquals(id_a, id_b)
+
+
+if __name__ == "__main__":
+ unittest.main()