"""
binary comparison
"""
import hashlib
import numpy as np
from .registry import ComparisonBase, EachCompareResult, register_comparison, FAIL_REASONS
@register_comparison(['bin', 'binary', 'binary_equal'])
class BinaryComparison(ComparisonBase):
STANDARD_NAME = "binary_equal"
def compare_impl(self) -> EachCompareResult:
if str(self.output.dtype) != str(self.golden.dtype):
return self._cross_dtype_compare()
diff_idx, golden_size, diff_idx_size = self._numpy_binary_compare(self.output, self.golden)
return self._result(diff_idx, golden_size, diff_idx_size)
def _result(self, diff_idx, golden_size, diff_idx_size):
if diff_idx_size == 0:
return EachCompareResult(1, is_pass=True, standard="binary_equal",
metrics={"standard": "binary_equal", "pass": True})
precision = (golden_size - diff_idx_size) / golden_size
return EachCompareResult(precision, diff_idx, is_pass=False, standard="binary_equal",
metrics={"standard": "binary_equal", "pass": False,
"reason": FAIL_REASONS["bitwise_mismatch"]})
def _cross_dtype_compare(self) -> EachCompareResult:
od, gd = self.output.dtype, self.golden.dtype
if any(x in str(d) for d in (od, gd) for x in ("int4", "float4")):
return self._reject(od, gd)
out_int = np.issubdtype(od, np.integer) or od.kind == "b"
gold_int = np.issubdtype(gd, np.integer) or gd.kind == "b"
if out_int and gold_int:
promoted = np.promote_types(od, gd)
if np.issubdtype(promoted, np.integer):
diff_idx, golden_size, diff_idx_size = self._numpy_binary_compare(
self.output.astype(promoted), self.golden.astype(promoted))
return self._result(diff_idx, golden_size, diff_idx_size)
return self._reject(od, gd)
def _reject(self, od, gd) -> EachCompareResult:
return EachCompareResult(0, is_pass=False, standard="binary_equal",
log=f"Dtype not bitwise-comparable: {od} vs {gd}",
metrics={"standard": "binary_equal", "pass": False,
"reason": FAIL_REASONS["cross_dtype_uncomparable"]})
@staticmethod
def _numpy_binary_compare(output: np.ndarray, golden: np.ndarray):
if not golden.flags['C_CONTIGUOUS']:
golden = np.ascontiguousarray(golden)
if 'float8_e8m0' in str(output.dtype):
output = output.view(np.uint8)
golden = golden.view(np.uint8)
if 'float4' in str(output.dtype):
output = output.view(np.int8)
golden = golden.view(np.int8)
if output.dtype.name not in ('bfloat16', 'int4', 'float8_e5m2', 'float8_e4m3fn', 'hifloat8'):
hash_output = hashlib.sha256(output.data).hexdigest()
hash_golden = hashlib.sha256(golden.data).hexdigest()
else:
hash_output = hashlib.sha256(output.tobytes()).hexdigest()
hash_golden = hashlib.sha256(golden.tobytes()).hexdigest()
if hash_output == hash_golden:
return None, golden.size, 0
output_int = output.view(dtype=np.uint8)
golden_int = golden.view(dtype=np.uint8)
same = output_int == golden_int
diff_idx = np.floor_divide(np.where(~same)[0], output.dtype.itemsize)
diff_idx = np.unique(diff_idx)
npu_nan, golden_nan = np.isnan(output), np.isnan(golden)
diff_nan = np.logical_and(npu_nan, golden_nan)
both_nan_idx = np.where(diff_nan)
diff_idx = np.setdiff1d(diff_idx, both_nan_idx)
del same, npu_nan, golden_nan, diff_nan
golden_size, diff_idx_size = golden.size, diff_idx.size
return diff_idx, golden_size, diff_idx_size