| # 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 array | |
| import gc | |
| import logging | |
| import numpy as np | |
| from pathlib import Path | |
| import tempfile | |
| import unittest | |
| import iree.runtime as rt | |
| def _float_constant(val: float) -> array.array: | |
| return array.array("f", [val]) | |
| def index_entry_as_array(entry, like_array): | |
| return np.asarray(entry.file_view).view(like_array.dtype).reshape(like_array.shape) | |
| class ParameterApiTest(unittest.TestCase): | |
| def testCreateArchiveFile(self): | |
| splat_index = rt.ParameterIndex() | |
| splat_index.add_splat("weight", _float_constant(2.0), 30 * 20 * 4) | |
| splat_index.add_splat("bias", _float_constant(1.0), 30 * 4) | |
| with tempfile.TemporaryDirectory() as td: | |
| file_path = Path(td) / "archive.irpa" | |
| target_index = splat_index.create_archive_file(str(file_path)) | |
| print(target_index) | |
| self.assertTrue(file_path.exists()) | |
| self.assertGreater(file_path.stat().st_size, 0) | |
| def testArchiveFileRoundtrip(self): | |
| orig_array = np.asarray([[1], [2], [3]], dtype=np.int64) | |
| def verify_archive(file_path: Path): | |
| # Load and verify. | |
| index = rt.ParameterIndex() | |
| # For this test, disable mmap to make temp file management on | |
| # windows a bit better. | |
| index.load(str(file_path), mmap=False) | |
| self.assertEqual(len(index), 3) | |
| # Note that the happy path of most properties are verified via | |
| # the repr (as they are called internal to that). | |
| entries = dict(index.items()) | |
| self.assertEqual( | |
| repr(entries["weight"]), | |
| "<ParameterIndexEntry 'weight' splat b'\\x02':600>", | |
| ) | |
| self.assertEqual( | |
| repr(entries["bias"]), | |
| "<ParameterIndexEntry 'bias' splat b' ':30>", | |
| ) | |
| self.assertRegex( | |
| repr(entries["array"]), | |
| r"<ParameterIndexEntry 'array' FileHandle<host_allocation\(.*\)>:384:24", | |
| ) | |
| # Verify some non-happy paths. | |
| with self.assertRaisesRegex(ValueError, "Entry is not file storage based"): | |
| entries["weight"].file_storage | |
| with self.assertRaisesRegex(ValueError, "Entry is not splat"): | |
| entries["array"].splat_pattern | |
| # Verify that the repr of the index itself is sensical. | |
| index_repr = repr(index) | |
| self.assertIn("Parameter scope <global> (3 entries", index_repr) | |
| # Get the array contents and verify against original. | |
| array_view = entries["array"].file_view | |
| self.assertEqual(len(array_view), 24) | |
| array_back = np.asarray(array_view).view(np.int64).reshape(orig_array.shape) | |
| np.testing.assert_array_equal(array_back, orig_array) | |
| with tempfile.TemporaryDirectory() as td: | |
| file_path = Path(td) / "archive.irpa" | |
| rt.save_archive_file( | |
| { | |
| "weight": rt.SplatValue(np.int8(2), [30, 20]), | |
| "bias": rt.SplatValue(array.array("b", [32]), 30), | |
| "array": orig_array, | |
| }, | |
| file_path, | |
| ) | |
| self.assertTrue(file_path.exists()) | |
| self.assertGreater(file_path.stat().st_size, 0) | |
| # Open / verify in its own scope and collect prior to tearing | |
| # down the temp dir. | |
| verify_archive(file_path) | |
| gc.collect() | |
| def testFileHandleWrap(self): | |
| fh = rt.FileHandle.wrap_memory(b"foobar") | |
| view = fh.host_allocation | |
| del fh | |
| self.assertEqual(bytes(view), b"foobar") | |
| def testParameterIndexAddFromFile(self): | |
| splat_index = rt.ParameterIndex() | |
| fh = rt.FileHandle.wrap_memory(b"foobar") | |
| splat_index.add_from_file_handle("data", fh, length=3, offset=3) | |
| def testSplatTooBig(self): | |
| splat_index = rt.ParameterIndex() | |
| with self.assertRaises(ValueError): | |
| splat_index.add_splat( | |
| "weight", array.array("f", [1.0, 2.0, 3.0, 4.0, 5.0]), 30 * 20 * 4 | |
| ) | |
| def testGguf(self): | |
| index = rt.ParameterIndex() | |
| index.load( | |
| str( | |
| Path(__file__).resolve().parent | |
| / "testdata" | |
| / "parameter_weight_bias_1.gguf" | |
| ) | |
| ) | |
| expected_weight = np.zeros([30, 20], dtype=np.float32) + 2.0 | |
| expected_bias = np.zeros([30], dtype=np.float32) + 1.0 | |
| entries = dict(index.items()) | |
| weight = index_entry_as_array(entries["weight"], expected_weight) | |
| bias = index_entry_as_array(entries["bias"], expected_bias) | |
| np.testing.assert_array_equal(weight, expected_weight) | |
| np.testing.assert_array_equal(bias, expected_bias) | |
| def testSafetensors(self): | |
| index = rt.ParameterIndex() | |
| index.load( | |
| str( | |
| Path(__file__).resolve().parent | |
| / "testdata" | |
| / "parameter_weight_bias_1.safetensors" | |
| ) | |
| ) | |
| expected_weight = np.zeros([30, 20], dtype=np.float32) + 2.0 | |
| expected_bias = np.zeros([30], dtype=np.float32) + 1.0 | |
| entries = dict(index.items()) | |
| weight = index_entry_as_array(entries["weight"], expected_weight) | |
| bias = index_entry_as_array(entries["bias"], expected_bias) | |
| np.testing.assert_array_equal(weight, expected_weight) | |
| np.testing.assert_array_equal(bias, expected_bias) | |
| if __name__ == "__main__": | |
| logging.basicConfig(level=logging.DEBUG) | |
| unittest.main() |