diff options
author | James Ward <james.ward@arm.com> | 2022-10-19 12:20:31 +0100 |
---|---|---|
committer | Jeremy Johnson <jeremy.johnson@arm.com> | 2022-11-09 12:19:51 +0000 |
commit | 24dbc420aae556649f50e645bd94489dab2cc75a (patch) | |
tree | 490345da43e9c5bae0f450ba05ffe85874077e0a /verif/generator/tosa_utils.py | |
parent | 3b0544c1e7463295c49a48a162ebb9a546326829 (diff) | |
download | reference_model-24dbc420aae556649f50e645bd94489dab2cc75a.tar.gz |
Add BF16 support to reference model
* Upgrade Eigen to 3.4.0 (for bfloat16 support) and add work-
arounds for reduce.any() and reduce.all() bugs (introduced
between 3.3.7 and 3.4.0)
* Truncation to bfloat16 now performed in eval() methods
Signed-off-by: James Ward <james.ward@arm.com>
Signed-off-by: Jeremy Johnson <jeremy.johnson@arm.com>
Change-Id: If5f5c988d76d3d30790acf3b97081726b89205fe
Diffstat (limited to 'verif/generator/tosa_utils.py')
-rw-r--r-- | verif/generator/tosa_utils.py | 45 |
1 files changed, 44 insertions, 1 deletions
diff --git a/verif/generator/tosa_utils.py b/verif/generator/tosa_utils.py index 104d9bb..d79ab3c 100644 --- a/verif/generator/tosa_utils.py +++ b/verif/generator/tosa_utils.py @@ -1,5 +1,9 @@ # Copyright (c) 2021-2022, ARM Limited. # SPDX-License-Identifier: Apache-2.0 +import struct +import sys + +import numpy as np from tosa.DType import DType # Maximum dimension size for output and inputs for RESIZE @@ -15,6 +19,7 @@ DTYPE_ATTRIBUTES = { DType.INT32: {"str": "i32", "width": 32}, DType.INT48: {"str": "i48", "width": 48}, DType.FP16: {"str": "f16", "width": 16}, + DType.BF16: {"str": "bf16", "width": 16}, DType.FP32: {"str": "f32", "width": 32}, } @@ -125,7 +130,11 @@ def get_wrong_output_type(op_name, rng, input_dtype): DType.FP32, DType.FP16, ) - elif input_dtype == DType.FP32 or input_dtype == DType.FP16: + elif ( + input_dtype == DType.FP32 + or input_dtype == DType.FP16 + or input_dtype == DType.BF16 + ): incorrect_types = ( DType.INT4, DType.INT8, @@ -134,3 +143,37 @@ def get_wrong_output_type(op_name, rng, input_dtype): DType.INT48, ) return rng.choice(a=incorrect_types) + + +def float32_is_valid_bfloat16(f): + """Return True if float value is valid bfloat16.""" + f32_bits = get_float32_bitstring(f) + return f32_bits[16:] == "0" * 16 + + +def get_float32_bitstring(f): + """Return a big-endian string of bits representing a 32 bit float.""" + f32_bits_as_int = struct.unpack(">L", struct.pack(">f", f))[0] + return f"{f32_bits_as_int:032b}" + + +def float32_to_bfloat16(f): + """Turns fp32 value into bfloat16 by flooring. + + Floors the least significant 16 bits of the input + fp32 value and returns this valid bfloat16 representation as fp32. + For simplicity during bit-wrangling, ignores underlying system + endianness and interprets as big-endian. + Returns a bf16-valid float following system's native byte order. + """ + f32_bits = get_float32_bitstring(f) + f32_floored_bits = f32_bits[:16] + "0" * 16 + + # Assume sys.byteorder matches system's underlying float byteorder + fp_bytes = int(f32_floored_bits, 2).to_bytes(4, byteorder=sys.byteorder) + return struct.unpack("@f", fp_bytes)[0] # native byteorder + + +vect_f32_to_bf16 = np.vectorize( + float32_to_bfloat16, otypes=(np.float32,) +) # NumPy vectorize: applies function to vector faster than looping |