Files
blis/gtestsuite/testsuite/inc/check_error.h
Vettickal Sen, Anuraj 4a9af35bf4 Bitexactness CRC verification and per-test JSON output (#320)
* Bitexactness CRC verification and per-test JSON output

* Remove redundant BLIS_TEST_SEED random seed utilities

The random_seed_utils.h and BLIS_TEST_SEED environment variable are unnecessary since the codebase already ensures deterministic random number generation via RANDOM_POOL_SEED and SRAND_SEED constants hardcoded in testing_helpers.h.

* Add CRC support for integer/char computediff and cache env var checks

Add CRC calculation and binary output to the gtint_t and char specializations of computediff, matching the pattern used by all other overloads. Char values are widened to gtint_t for safe uint32_t-aligned CRC access.

Cache BLIS_ENABLE_CRC and BLIS_ENABLE_BINARY_OUTPUT env var lookups via static const bool in is_crc_enabled() and is_binary_output_enabled(). Guard all CRC/binary blocks in computediff with is_any_verification_enabled() so the common disabled path is a single static bool read with zero allocations.

* Address PR review comments and refactor computediff CRC blocks

Refactor: Extract duplicated CRC/binary-output blocks from all 8 computediff overloads into verify_vector_data and verify_matrix_data helpers in blis_test_utils namespace.

Bug fixes from PR review: add missing includes (cstdlib, utility), enforce MAX_OUTPUT_SIZE_BYTES limit with integer overflow guard, add buffer validation in all CRC generation functions, add default case to FLA_GET_DATATYPE_FACTOR macro, replace deprecated test_case_name() with test_suite_name(), add MAKE_DIRECTORY error checking in CMake, and update copyright years to 2026.

* Refactored crc_utils based on review comments.

* binary_output_utils.h cleanup.

* Address PR review comments: remove unused functions and fix copyright years.

Remove unused generate_crc_matrix, generate_crc_matrix_no_nb_diag,
generate_crc_matrix_no_nb_diag_with_storage, and
calculate_and_print_matrix_crc from crc_utils.h.
Remove unused calculate_and_print_matrix_hash from check_error.h.
Fix copyright year to 2026 only in crc_utils.h and binary_output_utils.h.
Remove (Performance) label from CRC heading in README.md.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix for review comments.

* Address review comments: rename verify to collect, consistent void returns, remove filename prefix

- Rename verify_vector_data/verify_matrix_data to collect_vector_data/
  collect_matrix_data since these functions only collect CRC and binary
  output data without performing comparison.
- Make return types consistent: change calculate_and_print_crc,
  calculate_and_print_matrix_crc_with_storage, format_and_record_crc,
  and write_comparison_outputs to return void since return values were
  never used.
- Remove redundant test_output_ prefix from generate_binary_filename
  to avoid duplication with the blis_test_outputs/ directory.
- Remove unused utility include from binary_output_utils.h.
- Update README wording from compiled out to disabled.

Made-with: Cursor

* Fix strict aliasing, use if constexpr, zero-pad CRC hex, separate feature guards

- Replace reinterpret_cast<uint32_t*> with memcpy-based read_uint32()
  helper to avoid strict-aliasing UB on float/double/complex buffers.
  Produces identical CRC values.
- Use if constexpr(CRC_ENABLED) instead of runtime if(!CRC_ENABLED) to
  prevent CRC template instantiation when ENABLE_CRC is off.
- Zero-pad CRC hex output to 8 digits for stable downstream comparison.
- Separate ENABLE_CRC and ENABLE_BINARY_OUTPUT preprocessor guards in
  verification_utils.h so each feature is compiled independently.

Made-with: Cursor

* Handle write_binary_output return values in write_comparison_outputs

Capture the bool return values from write_binary_output and, on failure, log a warning to stdout and record the error as a GTest property. This keeps binary output as a non-fatal diagnostic aid while ensuring return values are explicitly used.

Made-with: Cursor

---------

Co-authored-by: Anuraj <avettick@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-03-13 15:12:17 +05:30

582 lines
29 KiB
C++

/*
BLIS
An object-based framework for developing high-performance BLAS-like
libraries.
Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name(s) of the copyright holder(s) nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "blis.h"
#include <gtest/gtest.h>
#include "common/testing_helpers.h"
#include "common/verification_utils.h"
/**
* This file includes the functionality used to determine correctness of the results.
* We compare the results component-wise, meaning that for two scalars, we compare the scalars,
* and for two vectors or matrices we compare each element of the vector or matrix respectively.
*
* We have two separate cases:
* 1) it's meaningful to have NaNs and/or Infs at the results,
* because we are testing the correct propagation of those values.
* In this case the results could be either extreme values, or f.p. values,
* and we need to be able to compare all of those following the rules below:
* - if there are NaNs/Infs, check if both reference and blis solution has
* NaN/Inf accordingly. Remember that for Infs we need to check the sign as well.
* - if there are no NaNs/Infs, either do the bitwise comparison (if that's desired),
* or call into getError() function. getError() will check if reference is less
* than one, in which case will compute the absolute error, otherwise compute the
* relative error.
* - for complex numbers, we need to check for all possible combinations of NaN/Inf/FP
* for real and imaginary parts. So that will be a combination of the two steps above.
* 2) it's not meaningful to check for NaNs and Infs and we expect only FP values in the
* results. In this case, we either do a bitwise comparison or call into getError() directly.
*
* Note that all operations with a NaN/Inf will lead to either comparison with a NaN, or
* inf < thresh, which always return false; so NumericalComparisonFPOnly() will return failure.
* So, for the case where we do not expect NaN/Infs, we want to fail if NaN and Infs are
* present so that we do not have tests passing when it doesn't make sense to do so.
* For an example of such case, think of a triangular solver with zeros on the diagonal.
*
*/
// Enum used to do the correct printing depending on what we aim to compare.
enum ObjType {
SCALAR,
VECTOR,
MATRIX
};
// Enum used to do the correct comparison for NaNs, depending on whether we
// compare the real or the imaginary component.
enum ComplexPart {
REAL,
IMAGINARY
};
// Helper class to be used to pass info into the Comparators.
struct ComparisonHelper{
double threshold;
ObjType object_type;
gtint_t i; // used to print vector/matrix elements that we compare
gtint_t j; // used to print matrix elements that we compare
bool binary_comparison; // By default compare using relative error or absolute error approach.
bool nan_inf_check; //By default do not check for NaNs and Infs.
// Constructor for the case of binary_comparison, no threshold.
ComparisonHelper(ObjType object_type) : threshold(-13.0),
object_type(object_type),
i(-11),
j(-11),
binary_comparison(false),
nan_inf_check(false) {};
// Constructor for the generic case where threshold is used.
ComparisonHelper(ObjType object_type, double threshold) : threshold(threshold),
object_type(object_type),
i(-11),
j(-11),
binary_comparison(false),
nan_inf_check(false) {};
};
// Generic comparison of f.p. numbers that doesn't check for NaN's and Infs:
template<typename T, typename ErrorMessageFunc>
testing::AssertionResult NumericalComparisonFPOnly(const char* blis_sol_char,
const char* ref_sol_char,
const char* comp_helper_char,
const T blis_sol,
const T ref_sol,
const ComparisonHelper comp_helper,
ErrorMessageFunc error_message_func)
{
if (comp_helper.binary_comparison)
{
if (blis_sol == ref_sol) return testing::AssertionSuccess();
return testing::AssertionFailure() << error_message_func();
}
else {
double error = testinghelpers::getError(blis_sol,ref_sol);
if (error <= comp_helper.threshold) return testing::AssertionSuccess();
using RT = typename testinghelpers::type_info<T>::real_type;
return testing::AssertionFailure() << error_message_func()
<< ", thresh = " << comp_helper.threshold
<< ", error = " << error
<< " (" << error/std::numeric_limits<RT>::epsilon() << " * eps)";
}
}
// NaN/Inf comparison for real numbers
template<typename T, typename ErrorMessageFunc>
testing::AssertionResult NumericalComparisonRealNaNInf(const char* blis_sol_char,
const char* ref_sol_char,
const char* comp_helper_char,
const T blis_sol,
const T ref_sol,
const ComparisonHelper comp_helper,
ErrorMessageFunc error_message_func)
{
// if both are NaN return SUCCESS
if ((std::isnan(ref_sol)) && (std::isnan(blis_sol)))
return testing::AssertionSuccess();
// if only one of them is NaN, return FAILURE
else if ((std::isnan(ref_sol)) || (std::isnan(blis_sol)))
return testing::AssertionFailure() << error_message_func();
// if both are inf check the sign
else if ((std::isinf(ref_sol)) && (std::isinf(blis_sol)))
{
// check the sign of infs
if( ref_sol == blis_sol ) return testing::AssertionSuccess();
// both are infs but have different signs, return FAILURE.
else return testing::AssertionFailure() << error_message_func();
}
// if only one of them is Inf
else if ((std::isinf(ref_sol)) || (std::isinf(blis_sol)))
return testing::AssertionFailure() << error_message_func();
// If neither reference nor BLIS sol is NaN/Inf do simple comparison, based on relative or absolute error.
else return NumericalComparisonFPOnly<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, error_message_func);
}
// Comparison for complex numbers in the case of NaNs.
// Will be re-used for comparison of real and imaginary components.
template<typename T, typename RT = typename testinghelpers::type_info<T>::real_type, typename ErrorMessageFunc>
testing::AssertionResult NumericalComparisonNaN(const char* blis_sol_char,
const char* ref_sol_char,
const char* comp_helper_char,
const T blis_sol,
const T ref_sol,
const ComparisonHelper comp_helper,
const ComplexPart complex_part,
ErrorMessageFunc error_message_func)
{
// Assign values to intermediate variables as if we are comparing the real part.
RT ref_sol_1 = ref_sol.real, ref_sol_2 = ref_sol.imag, blis_sol_1 = blis_sol.real, blis_sol_2 = blis_sol.imag;
// if we are comparing based on the imaginary part update the values.
if (complex_part == IMAGINARY)
{
ref_sol_2 = ref_sol.real;
ref_sol_1 = ref_sol.imag;
blis_sol_2 = blis_sol.real;
blis_sol_1 = blis_sol.imag;
}
// Check if the both parts are NaNs.
if ((std::isnan(ref_sol_1)) && (std::isnan(blis_sol_1)))
// Check second part for equality based on real NaN/Inf comparison.
return NumericalComparisonRealNaNInf<RT>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol_2, ref_sol_2, comp_helper, error_message_func);
// if only one of the first parts is NaN
return testing::AssertionFailure() << error_message_func();
}
// Comparison for complex numbers in the case of Infs.
// Will be re-used for comparison of real and imaginary components.
template<typename T, typename RT = typename testinghelpers::type_info<T>::real_type, typename ErrorMessageFunc>
testing::AssertionResult NumericalComparisonInf(const char* blis_sol_char,
const char* ref_sol_char,
const char* comp_helper_char,
const T blis_sol,
const T ref_sol,
const ComparisonHelper comp_helper,
const ComplexPart complex_part,
ErrorMessageFunc error_message_func)
{
// Assign values to intermediate variables as if we are comparing the real part.
RT ref_sol_1 = ref_sol.real, ref_sol_2 = ref_sol.imag, blis_sol_1 = blis_sol.real, blis_sol_2 = blis_sol.imag;
// if we are comparing based on the imaginary part update the values.
if (complex_part == IMAGINARY)
{
ref_sol_2 = ref_sol.real;
ref_sol_1 = ref_sol.imag;
blis_sol_2 = blis_sol.real;
blis_sol_1 = blis_sol.imag;
}
// check if both of the first parts are inf
if ((std::isinf(ref_sol_1)) && (std::isinf(blis_sol_1)))
{
// check the sign of infs
if( ref_sol_1 == blis_sol_1 )
// Check second part for equality based on real NaN/Inf comparison.
return NumericalComparisonRealNaNInf<RT>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol_2, ref_sol_2, comp_helper, error_message_func);
// if both are infs but have different signs, return FAILURE.
else return testing::AssertionFailure() << error_message_func();
}
// if only one of them is Inf
return testing::AssertionFailure() << error_message_func();
}
// Comparisons that take into account the presence of NaNs and Infs, printing variable name:
template<typename T, typename RT = typename testinghelpers::type_info<T>::real_type>
testing::AssertionResult NumericalComparison(const char* var_name_char,
const char* blis_sol_char,
const char* ref_sol_char,
const char* comp_helper_char,
std::string var_name,
const T blis_sol,
const T ref_sol,
const ComparisonHelper comp_helper)
{
// Lazy string construction - only create error message when actually needed
auto create_error_message = [&]() -> std::string {
// Base error message used for scalar values
std::string error_message = var_name_char;
error_message += " = " + var_name + ", ";
error_message += blis_sol_char;
error_message += " = " + testinghelpers::to_string(blis_sol) + ", ";
error_message += ref_sol_char;
error_message += " = " + testinghelpers::to_string(ref_sol);
// If we are comparing a vector, update error message to include the current index
if(comp_helper.object_type == VECTOR)
error_message += ", i = " + std::to_string(comp_helper.i);
// If we are comparing a matrix, update error message to include the current indices
else if(comp_helper.object_type == MATRIX)
error_message += ", i = " + std::to_string(comp_helper.i) + ", j = " + std::to_string(comp_helper.j);
return error_message;
};
// Check if NaN/Inf comparison is necessary and if so, proceed.
// Otherwise, call numerical comparison only, without considering NaNs and Infs.
if (comp_helper.nan_inf_check)
{
if constexpr (testinghelpers::type_info<T>::is_real)
return NumericalComparisonRealNaNInf<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, create_error_message);
// If it's complex we need to check real and imaginary parts.
else
{
// Check if any of the real parts is NaN, and if so, call into NaN comparator.
if ((std::isnan(ref_sol.real)) || (std::isnan(blis_sol.real)))
{
ComplexPart complex_part = REAL;
return NumericalComparisonNaN<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, complex_part, create_error_message);
}
// Check if any of the imag parts is NaN, and if so, call into NaN comparator.
else if ((std::isnan(ref_sol.imag)) || (std::isnan(blis_sol.imag)))
{
ComplexPart complex_part = IMAGINARY;
return NumericalComparisonNaN<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, complex_part, create_error_message);
}
// Check if any of the real parts is Inf, and if so, call into Inf comparator.
else if ((std::isinf(ref_sol.real)) || (std::isinf(blis_sol.real)))
{
ComplexPart complex_part = REAL;
return NumericalComparisonInf<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, complex_part, create_error_message);
}
// Check if any of the imag parts is NaN or Inf, and if so, call into Inf comparator.
else if ((std::isinf(ref_sol.imag)) || (std::isinf(blis_sol.imag)))
{
ComplexPart complex_part = IMAGINARY;
return NumericalComparisonInf<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, complex_part, create_error_message);
}
// If neither reference nor BLIS sol is NaN or Inf, or if NaN/Inf checks are not necessary,
// do simple comparison, based on relative or absolute error.
else
return NumericalComparisonFPOnly<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, create_error_message);
}
}
// If NaN/Inf checks are not necessary, do simple comparison, based on relative or absolute error.
else
return NumericalComparisonFPOnly<T>(blis_sol_char, ref_sol_char, comp_helper_char, blis_sol, ref_sol, comp_helper, create_error_message);
}
/**
* Binary comparison of two scalars, printing variable name.
*/
template <typename T>
void computediff( std::string var_name, T blis_sol, T ref_sol, bool nan_inf_check = false )
{
testinghelpers::verification::collect_vector_data(var_name, &blis_sol, &ref_sol, 1);
ComparisonHelper comp_helper(SCALAR);
comp_helper.binary_comparison = true;
comp_helper.nan_inf_check = nan_inf_check;
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol, ref_sol, comp_helper);
}
/**
* Relative comparison of two scalars, using a threshold, printing variable name.
*/
template <typename T>
void computediff( std::string var_name, T blis_sol, T ref_sol, double thresh, bool nan_inf_check = false )
{
testinghelpers::verification::collect_vector_data(var_name, &blis_sol, &ref_sol, 1);
ComparisonHelper comp_helper(SCALAR, thresh);
comp_helper.nan_inf_check = nan_inf_check;
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol, ref_sol, comp_helper);
}
/**
* Binary comparison of two vectors with length n and increment inc, printing variable name.
*/
template <typename T>
void computediff( std::string var_name, gtint_t n, T *blis_sol, T *ref_sol, gtint_t inc, bool nan_inf_check = false )
{
testinghelpers::verification::collect_vector_data(var_name, blis_sol, ref_sol, testinghelpers::buff_dim(n, inc));
gtint_t abs_inc = std::abs(inc);
ComparisonHelper comp_helper(VECTOR);
comp_helper.nan_inf_check = nan_inf_check;
comp_helper.binary_comparison = true;
// If increment is zero, we just have one element to compare.
if (abs_inc == 0)
n = 1;
// In case inc is negative in a call to BLIS APIs, we just access it from the end to the beginning,
// so practically nothing changes. Access from beginning to end to optimize memory operations.
for (gtint_t i = 0; i < n; i++)
{
comp_helper.i = i;
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*abs_inc], ref_sol[i*abs_inc], comp_helper) << "inc = " << inc;
// Go through elements that are part of the array that should not have been modified by the
// call to a BLIS API. Use the bitwise comparison for this case.
if (i < n-1)
{
for (gtint_t j = 1; j < abs_inc; j++)
{
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*abs_inc + j], ref_sol[i*abs_inc + j], comp_helper) << "inc = " << inc << " This element is expected to not be modified.";
}
}
}
}
/**
* Relative comparison of two vectors with length n and increment inc, printing variable name.
*/
template <typename T>
void computediff( std::string var_name, gtint_t n, T *blis_sol, T *ref_sol, gtint_t inc, double thresh, bool nan_inf_check = false )
{
testinghelpers::verification::collect_vector_data(var_name, blis_sol, ref_sol, testinghelpers::buff_dim(n, inc));
gtint_t abs_inc = std::abs(inc);
ComparisonHelper comp_helper(VECTOR, thresh);
comp_helper.nan_inf_check = nan_inf_check;
// If increment is zero, we just have one element to compare.
if (abs_inc == 0)
n = 1;
// In case inc is negative in a call to BLIS APIs, we just access it from the end to the beginning,
// so practically nothing changes. Access from beginning to end to optimize memory operations.
for (gtint_t i = 0; i < n; i++)
{
comp_helper.i = i;
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*abs_inc], ref_sol[i*abs_inc], comp_helper) << "inc = " << inc;
// Go through elements that are part of the array that should not have been modified by the
// call to a BLIS API. Use the bitwise comparison for this case.
if (i < n-1)
{
for (gtint_t j = 1; j < abs_inc; j++)
{
comp_helper.binary_comparison = true;
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*abs_inc + j], ref_sol[i*abs_inc + j], comp_helper) << "inc = " << inc << " This element is expected to not be modified.";
}
comp_helper.binary_comparison = false;
}
}
}
/**
* Binary comparison of two matrices with dimensions m-by-n and leading dimension ld, printing variable name.
*/
template <typename T>
void computediff(std::string var_name, char storage, gtint_t m, gtint_t n, T *blis_sol, T *ref_sol, gtint_t ld, bool nan_inf_check = false )
{
testinghelpers::verification::collect_matrix_data(var_name, blis_sol, ref_sol, m, n, ld, storage);
gtint_t i,j;
ComparisonHelper comp_helper(MATRIX);
comp_helper.nan_inf_check = nan_inf_check;
comp_helper.binary_comparison = true;
// Loop for column-major order
if( (storage == 'c') || (storage == 'C') )
{
for( j = 0 ; j < n ; j++ )
{
// First iterate through the elements of the arrays that are part of the matrix
// and are expected to be modified by a call to BLIS APIs.
for( i = 0 ; i < m ; i++ )
{
comp_helper.i = i;
comp_helper.j = j;
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i + j*ld], ref_sol[i + j*ld], comp_helper);
}
// Now iterate through the rest of elements in memory space that are not part of the matrix,
// so we use binary comparison to verify that are exactly the same as the reference.
// Since to get create the data we use a copy to initialize the reference results, those
// elements are expected to identical.
for (i = (std::max)(m,(gtint_t)0); i < ld; i++)
{
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i + j*ld], ref_sol[i + j*ld], comp_helper) << "This element is expected to not be modified.";
}
}
}
// Loop for row-major order
else
{
for( i = 0 ; i < m ; i++ )
{
// First iterate through the elements of the arrays that are part of the matrix
// and are expected to be modified by a call to BLIS APIs.
for( j = 0 ; j < n ; j++ )
{
comp_helper.i = i;
comp_helper.j = j;
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*ld + j], ref_sol[i*ld + j], comp_helper);
}
// Now iterate through the rest of elements in memory space that are not part of the matrix,
// so we use binary comparison to verify that are exactly the same as the reference.
// Since to get create the data we use a copy to initialize the reference results, those
// elements are expected to identical.
for (j = (std::max)(n,(gtint_t)0); j < ld; j++)
{
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*ld + j], ref_sol[i*ld + j], comp_helper) << "This element is expected to not be modified.";
}
}
}
}
/**
* Relative comparison of two matrices with dimensions m-by-n and leading dimension ld, printing variable name.
*/
template <typename T>
void computediff(std::string var_name, char storage, gtint_t m, gtint_t n, T *blis_sol, T *ref_sol, gtint_t ld, double thresh, bool nan_inf_check = false )
{
testinghelpers::verification::collect_matrix_data(var_name, blis_sol, ref_sol, m, n, ld, storage);
gtint_t i,j;
ComparisonHelper comp_helper(MATRIX, thresh);
comp_helper.nan_inf_check = nan_inf_check;
// Loop for column-major order
if( (storage == 'c') || (storage == 'C') )
{
for( j = 0 ; j < n ; j++ )
{
// First iterate through the elements of the arrays that are part of the matrix
// and are expected to be modified by a call to BLIS APIs.
for( i = 0 ; i < m ; i++ )
{
comp_helper.i = i;
comp_helper.j = j;
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i + j*ld], ref_sol[i + j*ld], comp_helper);
}
// Now iterate through the rest of elements in memory space that are not part of the matrix,
// so we use binary comparison to verify that are exactly the same as the reference.
// Since to get create the data we use a copy to initialize the reference results, those
// elements are expected to identical.
comp_helper.binary_comparison = true;
for (i = (std::max)(m,(gtint_t)0); i < ld; i++)
{
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i + j*ld], ref_sol[i + j*ld], comp_helper) << "This element is expected to not be modified.";
}
// Disable binary comparison before we go through the next column.
comp_helper.binary_comparison = false;
}
}
// Loop for row-major order
else
{
for( i = 0 ; i < m ; i++ )
{
// First iterate through the elements of the arrays that are part of the matrix
// and are expected to be modified by a call to BLIS APIs.
for( j = 0 ; j < n ; j++ )
{
comp_helper.i = i;
comp_helper.j = j;
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*ld + j], ref_sol[i*ld + j], comp_helper);
}
// Now iterate through the rest of elements in memory space that are not part of the matrix,
// so we use binary comparison to verify that are exactly the same as the reference.
// Since to get create the data we use a copy to initialize the reference results, those
// elements are expected to identical.
comp_helper.binary_comparison = true;
for (j = (std::max)(n,(gtint_t)0); j < ld; j++)
{
ASSERT_PRED_FORMAT4(NumericalComparison<T>, var_name, blis_sol[i*ld + j], ref_sol[i*ld + j], comp_helper) << "This element is expected to not be modified.";
}
// Disable binary comparison before we go through the next column.
comp_helper.binary_comparison = false;
}
}
}
// Generic comparison of integer numbers, printing variable name:
template<typename T>
testing::AssertionResult EqualityComparison(const char* var_name_char,
const char* blis_sol_char,
const char* ref_sol_char,
const char* comp_helper_char,
std::string var_name,
const T blis_sol,
const T ref_sol,
const ComparisonHelper comp_helper)
{
if (blis_sol == ref_sol) return testing::AssertionSuccess();
// Lazy string construction - only create when actually needed
auto create_error_message = [&]() -> std::string {
std::string error_message = var_name_char;
error_message += " = " + var_name + ", ";
error_message += blis_sol_char;
error_message += " = " + testinghelpers::to_string(blis_sol) + ", ";
error_message += ref_sol_char;
error_message += " = " + testinghelpers::to_string(ref_sol);
return error_message;
};
return testing::AssertionFailure() << create_error_message();
}
/**
* Comparison of two integers, printing variable name.
*/
template <>
inline void computediff<gtint_t>( std::string var_name, gtint_t blis_sol, gtint_t ref_sol, bool nan_inf_check )
{
testinghelpers::verification::collect_vector_data(var_name, &blis_sol, &ref_sol, 1);
ComparisonHelper comp_helper(SCALAR);
ASSERT_PRED_FORMAT4(EqualityComparison<gtint_t>, var_name, blis_sol, ref_sol, comp_helper);
}
/**
* Comparison of two characters, printing variable name.
*/
template <>
inline void computediff<char>( std::string var_name, char blis_sol, char ref_sol, bool nan_inf_check )
{
// Widen char to gtint_t for safe CRC calculation (avoids reading beyond 1-byte boundary)
gtint_t blis_val = static_cast<gtint_t>(blis_sol);
gtint_t ref_val = static_cast<gtint_t>(ref_sol);
testinghelpers::verification::collect_vector_data(var_name, &blis_val, &ref_val, 1);
ComparisonHelper comp_helper(SCALAR);
ASSERT_PRED_FORMAT4(EqualityComparison<char>, var_name, blis_sol, ref_sol, comp_helper);
}