Files
TheRock/rocm-libraries/projects/rocblas/clients/gtest/general_gtest.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1362 lines
67 KiB
C++

/* ************************************************************************
* Copyright (C) 2020-2025 Advanced Micro Devices, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop-
* ies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM-
* PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNE-
* CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* ************************************************************************ */
#include "rocblas_data.hpp"
#include "rocblas_matrix.hpp"
#include "rocblas_test.hpp"
#include "rocblas_vector.hpp"
#include "type_dispatch.hpp"
#include "unit.hpp"
#include "client_utility.hpp"
#include "../../library/src/include/check_numerics_matrix.hpp"
#include "../../library/src/include/check_numerics_vector.hpp"
namespace
{
template <typename T>
void expect_decimals_eq(T a, T b, int decimals)
{
EXPECT_EQ(std::round(a * pow(10, decimals)), std::round(b * pow(10, decimals)));
}
//
// half floats
template <typename T>
void testing_half_operators(const Arguments& arg)
{
T c(0.5);
T s(2.0);
T result = -(c + c) * s;
result /= s;
EXPECT_EQ(result, T(-1.0));
c = T(0.5);
s = T(2.0);
result = c * s + s / c;
EXPECT_EQ((float)result, 5.0f);
// unique harmonic convergence
// search half-precision-arithmetic-fp16-versus-bfloat16 harmonic
result = T(0);
if(std::is_same_v<T, rocblas_half>)
{
for(int i = 1; i <= 513; i++)
result += T(1.0) / T(i);
expect_decimals_eq((float)result, 7.08594f, 5);
}
else if(std::is_same_v<T, rocblas_bfloat16>)
{
for(int i = 1; i <= 65; i++)
result += T(1.0) / T(i);
expect_decimals_eq((float)result, 5.0625f, 4);
}
};
// By default, arbitrary type combinations are invalid.
// The unnamed second parameter is used for enable_if_t below.
template <typename, typename = void>
struct half_operators_testing : rocblas_test_invalid
{
};
template <typename T>
struct half_operators_testing<
T,
std::enable_if_t<std::is_same_v<T, rocblas_half> || std::is_same_v<T, rocblas_bfloat16>>>
: rocblas_test_valid
{
void operator()(const Arguments& arg)
{
if(!strcmp(arg.function, "half_operators"))
testing_half_operators<T>(arg);
else
FAIL() << "Internal error: Test called with unknown function: " << arg.function;
}
};
struct half_operators : RocBLAS_Test<half_operators, half_operators_testing>
{
// Filter for which types apply to this suite
static bool type_filter(const Arguments& arg)
{
return true;
}
// Filter for which functions apply to this suite
static bool function_filter(const Arguments& arg)
{
return !strcmp(arg.function, "half_operators");
}
// Google Test name suffix based on parameters
static std::string name_suffix(const Arguments& arg)
{
RocBLAS_TestName<half_operators> name(arg.name);
name << rocblas_datatype2string(arg.a_type);
return std::move(name);
}
};
TEST_P(half_operators, auxiliary)
{
CATCH_SIGNALS_AND_EXCEPTIONS_AS_FAILURES(
rocblas_simple_dispatch<half_operators_testing>(GetParam()));
}
INSTANTIATE_TEST_CATEGORIES(half_operators);
//
// complex
template <typename T>
void testing_complex_operators(const Arguments& arg)
{
using R = real_t<T>;
T c(0.5, 0.25);
R s(2.0);
T result = c * s;
EXPECT_EQ(result, T(1.0, 0.5));
result /= s;
EXPECT_EQ(result, c);
T val(1.0, -2.0);
result = (s - val) / s;
EXPECT_EQ(result, T(0.5, 1.0));
result = T(20.0, -4.0) / T(3.0, 2.0);
EXPECT_EQ(result, T(4.0, -4.0));
result = 1.0 / T(1.0, 0.0);
EXPECT_EQ(result, T(1.0, 0.0));
int exact_zeros = 0;
for(int i = 0; i < 10; i++)
{
T r = random_hpl_generator<T>();
if(r.real() == 0.0 || r.imag() == 0.0)
exact_zeros++;
}
EXPECT_LT(exact_zeros, 2);
};
// By default, arbitrary type combinations are invalid.
// The unnamed second parameter is used for enable_if_t below.
template <typename, typename = void>
struct complex_operators_testing : rocblas_test_invalid
{
};
template <typename T>
struct complex_operators_testing<
T,
std::enable_if_t<
std::is_same_v<T, rocblas_float_complex> || std::is_same_v<T, rocblas_double_complex>>>
: rocblas_test_valid
{
void operator()(const Arguments& arg)
{
if(!strcmp(arg.function, "complex_operators"))
testing_complex_operators<T>(arg);
else
FAIL() << "Internal error: Test called with unknown function: " << arg.function;
}
};
struct complex_operators : RocBLAS_Test<complex_operators, complex_operators_testing>
{
// Filter for which types apply to this suite
static bool type_filter(const Arguments& arg)
{
return true;
}
// Filter for which functions apply to this suite
static bool function_filter(const Arguments& arg)
{
return !strcmp(arg.function, "complex_operators");
}
// Google Test name suffix based on parameters
static std::string name_suffix(const Arguments& arg)
{
RocBLAS_TestName<complex_operators> name(arg.name);
name << rocblas_datatype2string(arg.a_type);
return std::move(name);
}
};
TEST_P(complex_operators, auxiliary)
{
CATCH_SIGNALS_AND_EXCEPTIONS_AS_FAILURES(
rocblas_simple_dispatch<complex_operators_testing>(GetParam()));
}
INSTANTIATE_TEST_CATEGORIES(complex_operators);
//
// helper utilities
template <typename T>
void testing_helper_utilities(const Arguments& arg)
{
// just want to ensure 64 bit math
uint32_t block = 8;
int64_t stride = int64_t(1) << 33; // int32 overflow
int64_t offset = stride + 2;
T val(42);
const T* valptr = &val - block * stride;
T result = load_scalar(valptr, block, stride);
EXPECT_EQ(result, val);
const T* dst = load_ptr_batch(&val, block, offset, stride);
valptr = &val + block * stride + offset;
EXPECT_EQ(dst, valptr);
dst = load_ptr_batch(&val, block, stride);
valptr = &val + block * stride;
EXPECT_EQ(dst, valptr);
};
// By default, arbitrary type combinations are invalid.
// The unnamed second parameter is used for enable_if_t below.
template <typename, typename = void>
struct helper_utilities_testing : rocblas_test_invalid
{
};
template <typename T>
struct helper_utilities_testing<
T,
std::enable_if_t<
std::is_same_v<
T,
rocblas_half> || std::is_same_v<T, rocblas_bfloat16> || std::is_same_v<T, rocblas_float_complex> || std::is_same_v<T, rocblas_double_complex> || std::is_same_v<T, float> || std::is_same_v<T, double>>>
: rocblas_test_valid
{
void operator()(const Arguments& arg)
{
if(!strcmp(arg.function, "helper_utilities"))
testing_helper_utilities<T>(arg);
else
FAIL() << "Internal error: Test called with unknown function: " << arg.function;
}
};
struct helper_utilities : RocBLAS_Test<helper_utilities, helper_utilities_testing>
{
// Filter for which types apply to this suite
static bool type_filter(const Arguments& arg)
{
return true;
}
// Filter for which functions apply to this suite
static bool function_filter(const Arguments& arg)
{
return !strcmp(arg.function, "helper_utilities");
}
// Google Test name suffix based on parameters
static std::string name_suffix(const Arguments& arg)
{
RocBLAS_TestName<helper_utilities> name(arg.name);
name << rocblas_datatype2string(arg.a_type);
return std::move(name);
}
};
TEST_P(helper_utilities, auxiliary)
{
CATCH_SIGNALS_AND_EXCEPTIONS_AS_FAILURES(
rocblas_simple_dispatch<helper_utilities_testing>(GetParam()));
}
INSTANTIATE_TEST_CATEGORIES(helper_utilities);
//
// check numerics
//
//Testing a vector for NaN/zero/Inf/denormal values
template <typename T>
void testing_check_numerics_vector(const Arguments& arg)
{
int64_t N = arg.N;
int64_t inc_x = arg.incx;
rocblas_stride offset_x = 0;
rocblas_stride stride_x = arg.stride_x;
int64_t batch_count = arg.batch_count;
// for initialization we don't care about computation order
size_t abs_inc_x = std::abs(inc_x);
//Creating a rocBLAS handle
rocblas_local_handle handle{arg};
rocblas_status status = rocblas_status_success;
const char function_name[] = "testing_check_numerics_vector";
bool is_input = true;
//Hard-code the enum `check_numerics` to `rocblas_check_numerics_mode_fail` which will return `rocblas_status_check_numerics_fail` if the vector contains a NaN/Inf/denormal value
rocblas_check_numerics_mode check_numerics = rocblas_check_numerics_mode_fail;
//Argument sanity check before allocating invalid memory
if(N <= 0 || batch_count <= 0)
{
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
(T*)nullptr,
offset_x,
inc_x,
stride_x,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
return;
}
//Allocating memory for the host vector
host_vector<T> h_x(N, inc_x);
// allocate memory on device
device_vector<T> d_x(N, inc_x);
CHECK_DEVICE_ALLOCATION(d_x.memcheck());
//==============================================================================================
// Initializing random values in the vector
//==============================================================================================
rocblas_init_vector(h_x, arg, rocblas_client_never_set_nan, true);
// copy data from CPU to device
CHECK_HIP_ERROR(d_x.transfer_from(h_x));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
(T*)d_x,
offset_x,
inc_x,
stride_x,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
//==============================================================================================
// Initializing and testing for zero in the vector
//==============================================================================================
rocblas_init_zero_range<T>((T*)h_x, 0, 1); // for inc_x==0 case we set first element
// copy data from CPU to device
CHECK_HIP_ERROR(d_x.transfer_from(h_x));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
(T*)d_x,
offset_x,
inc_x,
stride_x,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
//==============================================================================================
// Initializing and testing for Inf in the vector
//==============================================================================================
rocblas_init_vector(h_x, arg, rocblas_client_never_set_nan, true);
rocblas_init_inf_range<T>((T*)h_x, 0, 1);
// copy data from CPU to device
CHECK_HIP_ERROR(d_x.transfer_from(h_x));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
(T*)d_x,
offset_x,
inc_x,
stride_x,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
//==============================================================================================
// Initializing and testing for NaN in the vector
//==============================================================================================
rocblas_init_vector(h_x, arg, rocblas_client_never_set_nan, true);
rocblas_init_nan_range<T>((T*)h_x, 0, 1);
// copy data from CPU to device
CHECK_HIP_ERROR(d_x.transfer_from(h_x));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
(T*)d_x,
offset_x,
inc_x,
stride_x,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
//==============================================================================================
// Initializing and testing for denorm values in the vector
//==============================================================================================
rocblas_init_vector(h_x, arg, rocblas_client_never_set_nan, true);
rocblas_init_denorm_range<T>((T*)h_x, 0, 1);
// copy data from CPU to device
CHECK_HIP_ERROR(d_x.transfer_from(h_x));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
(T*)d_x,
offset_x,
inc_x,
stride_x,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
//==============================================================================================
// Initializing random values in batched vectors
//==============================================================================================
//Allocate device and host batched vectors
device_batch_vector<T> d_x_batch(N, inc_x, batch_count);
host_batch_vector<T> h_x_batch(N, inc_x, batch_count);
//Initialize Data on CPU
rocblas_init_vector(h_x_batch, arg, rocblas_client_never_set_nan, true);
//Transferring data from host to device
CHECK_HIP_ERROR(d_x_batch.transfer_from(h_x_batch));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
d_x_batch.const_batch_ptr(),
offset_x,
inc_x,
stride_x,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
//==============================================================================================
// Initializing and testing for zero in batched vectors
//==============================================================================================
//Initialize Data on CPU
rocblas_init_vector(h_x_batch, arg, rocblas_client_never_set_nan, true);
for(size_t i = batch_count - 1; i < batch_count; i++)
for(size_t j = N - 1; j < N; j++)
h_x_batch[i][j * abs_inc_x] = T(rocblas_zero_rng());
//Transferring data from host to device
CHECK_HIP_ERROR(d_x_batch.transfer_from(h_x_batch));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
d_x_batch.const_batch_ptr(),
offset_x,
inc_x,
stride_x,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
//==============================================================================================
// Initializing and testing for Inf in batched vectors
//==============================================================================================
//Initialize Data on CPU
rocblas_init_vector(h_x_batch, arg, rocblas_client_never_set_nan, true);
for(size_t i = batch_count - 1; i < batch_count; i++)
for(size_t j = N - 1; j < N; j++)
h_x_batch[i][j * abs_inc_x] = T(rocblas_inf_rng());
//Transferring data from host to device
CHECK_HIP_ERROR(d_x_batch.transfer_from(h_x_batch));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
d_x_batch.const_batch_ptr(),
offset_x,
inc_x,
stride_x,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
//==============================================================================================
// Initializing and testing for NaN in batched vectors
//==============================================================================================
//Initialize Data on CPU
rocblas_init_vector(h_x_batch, arg, rocblas_client_never_set_nan, true);
for(size_t i = batch_count - 1; i < batch_count; i++)
for(size_t j = N - 1; j < N; j++)
h_x_batch[i][j * abs_inc_x] = T(rocblas_nan_rng());
//Transferring data from host to device
CHECK_HIP_ERROR(d_x_batch.transfer_from(h_x_batch));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
d_x_batch.const_batch_ptr(),
offset_x,
inc_x,
stride_x,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
//==============================================================================================
// Initializing and testing for denorm values in batched vectors
//==============================================================================================
//Initialize Data on CPU
rocblas_init_vector(h_x_batch, arg, rocblas_client_never_set_nan, true);
for(size_t i = batch_count - 1; i < batch_count; i++)
for(size_t j = N - 1; j < N; j++)
h_x_batch[i][j * abs_inc_x] = T(rocblas_denorm_rng());
//Transferring data from host to device
CHECK_HIP_ERROR(d_x_batch.transfer_from(h_x_batch));
status = rocblas_internal_check_numerics_vector_template(function_name,
handle,
N,
d_x_batch.const_batch_ptr(),
offset_x,
inc_x,
stride_x,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
};
// By default, arbitrary type combinations are invalid.
// The unnamed second parameter is used for enable_if_t below.
template <typename, typename = void>
struct check_numerics_vector_testing : rocblas_test_invalid
{
};
template <typename T>
struct check_numerics_vector_testing<
T,
std::enable_if_t<
std::is_same_v<
T,
rocblas_half> || std::is_same_v<T, rocblas_bfloat16> || std::is_same_v<T, rocblas_float_complex> || std::is_same_v<T, rocblas_double_complex> || std::is_same_v<T, float> || std::is_same_v<T, double>>>
: rocblas_test_valid
{
void operator()(const Arguments& arg)
{
if(!strcmp(arg.function, "check_numerics_vector"))
testing_check_numerics_vector<T>(arg);
else
FAIL() << "Internal error: Test called with unknown function: " << arg.function;
}
};
struct check_numerics_vector
: RocBLAS_Test<check_numerics_vector, check_numerics_vector_testing>
{
// Filter for which types apply to this suite
static bool type_filter(const Arguments& arg)
{
return true;
}
// Filter for which functions apply to this suite
static bool function_filter(const Arguments& arg)
{
return !strcmp(arg.function, "check_numerics_vector");
}
// Google Test name suffix based on parameters
static std::string name_suffix(const Arguments& arg)
{
RocBLAS_TestName<check_numerics_vector> name(arg.name);
name << rocblas_datatype2string(arg.a_type);
name << '_' << arg.N << '_' << arg.incx;
name << '_' << arg.batch_count;
return std::move(name);
}
};
TEST_P(check_numerics_vector, auxiliary)
{
CATCH_SIGNALS_AND_EXCEPTIONS_AS_FAILURES(
rocblas_simple_dispatch<check_numerics_vector_testing>(GetParam()));
}
INSTANTIATE_TEST_CATEGORIES(check_numerics_vector);
//Testing a matrix for NaN/zero/Inf/denormal values
template <typename T>
void testing_check_numerics_matrix(const Arguments& arg)
{
int64_t M = arg.M;
int64_t N = arg.N;
int64_t lda = arg.lda;
rocblas_stride offset_a = 0;
rocblas_stride stride_a = arg.stride_a;
int64_t batch_count = arg.batch_count;
rocblas_fill uplo = char2rocblas_fill(arg.uplo);
rocblas_operation transA = char2rocblas_operation(arg.transA);
int64_t row = transA == rocblas_operation_none ? M : N;
int64_t col = transA == rocblas_operation_none ? N : M;
lda = std::max(M, lda);
rocblas_local_handle handle{arg};
//Hard-code the enum `check_numerics` to `rocblas_check_numerics_mode_fail` which will return `rocblas_status_check_numerics_fail` if the matrix contains a NaN/Inf/denormal value
rocblas_check_numerics_mode check_numerics = rocblas_check_numerics_mode_fail;
rocblas_status status = rocblas_status_success;
const char function_name[] = "testing_check_numerics_matrix";
bool is_input = true;
//Argument sanity check before allocating invalid memory
if(!M || !N || !batch_count)
return;
//Allocating memory for the host matrix
host_matrix<T> h_A(row, col, lda);
// triangular and symmetric will be based on columns matching rocblas_init_matrix
host_matrix<T> h_A_symmetric(col, col, lda);
host_matrix<T> h_A_triangular(col, col, lda);
// Allocate memory on device
device_matrix<T> d_A(row, col, lda);
CHECK_DEVICE_ALLOCATION(d_A.memcheck());
device_matrix<T> d_A_triangular(col, col, lda);
CHECK_DEVICE_ALLOCATION(d_A_triangular.memcheck());
device_matrix<T> d_A_symmetric(col, col, lda);
CHECK_DEVICE_ALLOCATION(d_A_symmetric.memcheck());
//==============================================================================================
// Initializing random values in the matrix
//==============================================================================================
rocblas_init_matrix(
h_A, arg, rocblas_client_never_set_nan, rocblas_client_general_matrix, true);
rocblas_init_matrix(h_A_triangular,
arg,
rocblas_client_never_set_nan,
rocblas_client_triangular_matrix,
true);
rocblas_init_matrix(h_A_symmetric,
arg,
rocblas_client_never_set_nan,
rocblas_client_symmetric_matrix,
true);
CHECK_HIP_ERROR(d_A.transfer_from(h_A));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
(T*)d_A,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
CHECK_HIP_ERROR(d_A_triangular.transfer_from(h_A_triangular));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
uplo,
rocblas_client_triangular_matrix,
col,
col,
(T*)d_A_triangular,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
CHECK_HIP_ERROR(d_A_symmetric.transfer_from(h_A_symmetric));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
uplo,
rocblas_client_symmetric_matrix,
col,
col,
(T*)d_A_symmetric,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
//==============================================================================================
// Initializing and testing for zero in the matrix
//==============================================================================================
rocblas_init_zero<T>((T*)h_A, row, col, lda);
rocblas_init_zero_range<T>((T*)h_A_triangular, 0, 1);
// copy data from CPU to device
CHECK_HIP_ERROR(d_A.transfer_from(h_A));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
(T*)d_A,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
CHECK_HIP_ERROR(d_A_triangular.transfer_from(h_A_triangular));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
uplo,
rocblas_client_triangular_matrix,
col,
col,
(T*)d_A_triangular,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
//==============================================================================================
// Initializing and testing for Inf in the matrix
//==============================================================================================
rocblas_init_matrix(
h_A, arg, rocblas_client_never_set_nan, rocblas_client_general_matrix, true);
rocblas_init_inf_range<T>((T*)h_A, row - 1, row);
rocblas_init_inf_range<T>((T*)h_A_symmetric, 0, col);
CHECK_HIP_ERROR(d_A.transfer_from(h_A));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
(T*)d_A,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
CHECK_HIP_ERROR(d_A_symmetric.transfer_from(h_A_symmetric));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
uplo,
rocblas_client_symmetric_matrix,
col,
col,
(T*)d_A_symmetric,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
//==============================================================================================
// Initializing and testing for NaN in the matrix
//==============================================================================================
rocblas_init_matrix(
h_A, arg, rocblas_client_never_set_nan, rocblas_client_general_matrix, true);
rocblas_init_matrix(h_A_triangular,
arg,
rocblas_client_never_set_nan,
rocblas_client_triangular_matrix,
true);
rocblas_init_nan<T>((T*)h_A, row, col, lda);
rocblas_init_nan<T>((T*)h_A_triangular, col, col, lda);
CHECK_HIP_ERROR(d_A.transfer_from(h_A));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
(T*)d_A,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
CHECK_HIP_ERROR(d_A_triangular.transfer_from(h_A_triangular));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
uplo,
rocblas_client_triangular_matrix,
col,
col,
(T*)d_A_triangular,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
//==============================================================================================
// Initializing and testing for denorm values in the matrix
//==============================================================================================
rocblas_init_matrix(
h_A, arg, rocblas_client_never_set_nan, rocblas_client_general_matrix, true);
rocblas_init_matrix(h_A_symmetric,
arg,
rocblas_client_never_set_nan,
rocblas_client_symmetric_matrix,
true);
rocblas_init_denorm<T>((T*)h_A, row, col, lda);
rocblas_init_denorm<T>((T*)h_A_symmetric, col, col, lda);
// copy data from CPU to device
CHECK_HIP_ERROR(d_A.transfer_from(h_A));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
(T*)d_A,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
CHECK_HIP_ERROR(d_A_symmetric.transfer_from(h_A_symmetric));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
uplo,
rocblas_client_symmetric_matrix,
col,
col,
(T*)d_A_symmetric,
offset_a,
lda,
stride_a,
1,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
}
//Testing a matrix for NaN/zero/Inf/denormal values
template <typename T>
void testing_check_numerics_matrix_batched(const Arguments& arg)
{
int64_t M = arg.M;
int64_t N = arg.N;
int64_t lda = arg.lda;
rocblas_stride offset_a = 0;
rocblas_stride stride_a = arg.stride_a;
int64_t batch_count = arg.batch_count;
rocblas_fill uplo = char2rocblas_fill(arg.uplo);
rocblas_operation transA = char2rocblas_operation(arg.transA);
int64_t row = transA == rocblas_operation_none ? M : N;
int64_t col = transA == rocblas_operation_none ? N : M;
lda = std::max(M, lda);
rocblas_local_handle handle{arg};
//Hard-code the enum `check_numerics` to `rocblas_check_numerics_mode_fail` which will return `rocblas_status_check_numerics_fail` if the matrix contains a NaN/Inf/denormal value
rocblas_check_numerics_mode check_numerics = rocblas_check_numerics_mode_fail;
rocblas_status status = rocblas_status_success;
const char function_name[] = "testing_check_numerics_matrix";
bool is_input = true;
//Argument sanity check before allocating invalid memory
if(!M || !N || !batch_count)
return;
//==============================================================================================
// Initializing random values in batched matrices
//==============================================================================================
//Allocating memory for the host batch matrix
host_batch_matrix<T> h_A_batch(row, col, lda, batch_count);
// triangular and symmetric will be based on columns matching rocblas_init_matrix
host_batch_matrix<T> h_A_batch_triangular(col, col, lda, batch_count);
host_batch_matrix<T> h_A_batch_symmetric(col, col, lda, batch_count);
// Allocate memory on device
device_batch_matrix<T> d_A_batch(row, col, lda, batch_count);
CHECK_DEVICE_ALLOCATION(d_A_batch.memcheck());
device_batch_matrix<T> d_A_batch_triangular(col, col, lda, batch_count);
CHECK_DEVICE_ALLOCATION(d_A_batch_triangular.memcheck());
device_batch_matrix<T> d_A_batch_symmetric(col, col, lda, batch_count);
CHECK_DEVICE_ALLOCATION(d_A_batch_symmetric.memcheck());
//Initialize Data on CPU
rocblas_init_matrix(
h_A_batch, arg, rocblas_client_never_set_nan, rocblas_client_general_matrix, true);
rocblas_init_matrix(h_A_batch_triangular,
arg,
rocblas_client_never_set_nan,
rocblas_client_triangular_matrix,
true);
rocblas_init_matrix(h_A_batch_symmetric,
arg,
rocblas_client_never_set_nan,
rocblas_client_symmetric_matrix,
true);
//Transferring data from host to device
CHECK_HIP_ERROR(d_A_batch.transfer_from(h_A_batch));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
d_A_batch.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
CHECK_HIP_ERROR(d_A_batch_symmetric.transfer_from(h_A_batch_symmetric));
status
= rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
uplo,
rocblas_client_symmetric_matrix,
col,
col,
d_A_batch_symmetric.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
//==============================================================================================
// Initializing and testing for zero in batched matrices
//==============================================================================================
//Initialize Data on CPU
rocblas_init_matrix(
h_A_batch, arg, rocblas_client_never_set_nan, rocblas_client_general_matrix, true);
rocblas_init_matrix(h_A_batch_triangular,
arg,
rocblas_client_never_set_nan,
rocblas_client_triangular_matrix,
true);
for(size_t i_batch = 0; i_batch < batch_count; i_batch++)
for(size_t j = 0; j < col; ++j)
for(size_t i = 0; i < row; ++i)
{
h_A_batch[i_batch][i + j * lda] = T(rocblas_zero_rng());
if(i < col)
h_A_batch_triangular[i_batch][i + j * lda] = T(rocblas_zero_rng());
}
//Transferring data from host to device
CHECK_HIP_ERROR(d_A_batch.transfer_from(h_A_batch));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
d_A_batch.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
CHECK_HIP_ERROR(d_A_batch_triangular.transfer_from(h_A_batch_triangular));
status = rocblas_internal_check_numerics_matrix_template(
function_name,
handle,
transA,
uplo,
rocblas_client_triangular_matrix,
col,
col,
d_A_batch_triangular.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_success);
//==============================================================================================
// Initializing and testing for Inf in batched matrices
//==============================================================================================
//Initialize Data on CPU
rocblas_init_matrix(
h_A_batch, arg, rocblas_client_never_set_nan, rocblas_client_general_matrix, true);
rocblas_init_matrix(h_A_batch_symmetric,
arg,
rocblas_client_never_set_nan,
rocblas_client_symmetric_matrix,
true);
for(size_t i_batch = 0; i_batch < batch_count; i_batch++)
for(size_t j = 0; j < col; ++j)
for(size_t i = 0; i < row; ++i)
{
h_A_batch[i_batch][i + j * lda] = T(rocblas_inf_rng());
if(i < col)
h_A_batch_symmetric[i_batch][i + j * lda] = T(rocblas_inf_rng());
}
//Transferring data from host to device
CHECK_HIP_ERROR(d_A_batch.transfer_from(h_A_batch));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
d_A_batch.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
CHECK_HIP_ERROR(d_A_batch_symmetric.transfer_from(h_A_batch_symmetric));
status
= rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
uplo,
rocblas_client_symmetric_matrix,
col,
col,
d_A_batch_symmetric.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
//==============================================================================================
// Initializing and testing for NaN in batched matrices
//==============================================================================================
//Initialize Data on CPU
rocblas_init_matrix(
h_A_batch, arg, rocblas_client_never_set_nan, rocblas_client_general_matrix, true);
rocblas_init_matrix(h_A_batch_triangular,
arg,
rocblas_client_never_set_nan,
rocblas_client_triangular_matrix,
true);
for(size_t i_batch = 0; i_batch < batch_count; i_batch++)
for(size_t j = 0; j < col; ++j)
for(size_t i = 0; i < row; ++i)
{
h_A_batch[i_batch][i + j * lda] = T(rocblas_nan_rng());
if(i < col)
h_A_batch_triangular[i_batch][i + j * lda] = T(rocblas_nan_rng());
}
CHECK_HIP_ERROR(d_A_batch.transfer_from(h_A_batch));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
d_A_batch.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
CHECK_HIP_ERROR(d_A_batch_triangular.transfer_from(h_A_batch_triangular));
status = rocblas_internal_check_numerics_matrix_template(
function_name,
handle,
transA,
uplo,
rocblas_client_triangular_matrix,
col,
col,
d_A_batch_triangular.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
//==============================================================================================
// Initializing and testing for denorm values in batched matrices
//==============================================================================================
//Initialize Data on CPU
rocblas_init_matrix(
h_A_batch, arg, rocblas_client_never_set_nan, rocblas_client_general_matrix, true);
rocblas_init_matrix(h_A_batch_symmetric,
arg,
rocblas_client_never_set_nan,
rocblas_client_symmetric_matrix,
true);
for(size_t i_batch = 0; i_batch < batch_count; i_batch++)
for(size_t j = 0; j < col; ++j)
for(size_t i = 0; i < row; ++i)
{
h_A_batch[i_batch][i + j * lda] = T(rocblas_denorm_rng());
if(i < col)
h_A_batch_symmetric[i_batch][i + j * lda] = T(rocblas_denorm_rng());
}
CHECK_HIP_ERROR(d_A_batch.transfer_from(h_A_batch));
status = rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
rocblas_fill_full,
rocblas_client_general_matrix,
M,
N,
d_A_batch.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
CHECK_HIP_ERROR(d_A_batch_symmetric.transfer_from(h_A_batch_symmetric));
status
= rocblas_internal_check_numerics_matrix_template(function_name,
handle,
transA,
uplo,
rocblas_client_symmetric_matrix,
col,
col,
d_A_batch_symmetric.const_batch_ptr(),
offset_a,
lda,
stride_a,
batch_count,
check_numerics,
is_input);
EXPECT_EQ(status, rocblas_status_check_numerics_fail);
}
// By default, arbitrary type combinations are invalid.
// The unnamed second parameter is used for enable_if_t below.
template <typename, typename = void>
struct check_numerics_matrix_testing : rocblas_test_invalid
{
};
template <typename T>
struct check_numerics_matrix_testing<
T,
std::enable_if_t<
std::is_same_v<
T,
rocblas_half> || std::is_same_v<T, rocblas_bfloat16> || std::is_same_v<T, rocblas_float_complex> || std::is_same_v<T, rocblas_double_complex> || std::is_same_v<T, float> || std::is_same_v<T, double>>>
: rocblas_test_valid
{
void operator()(const Arguments& arg)
{
if(!strcmp(arg.function, "check_numerics_matrix"))
testing_check_numerics_matrix<T>(arg);
else if(!strcmp(arg.function, "check_numerics_matrix_batched"))
testing_check_numerics_matrix_batched<T>(arg);
else
FAIL() << "Internal error: Test called with unknown function: " << arg.function;
}
};
struct check_numerics_matrix
: RocBLAS_Test<check_numerics_matrix, check_numerics_matrix_testing>
{
// Filter for which types apply to this suite
static bool type_filter(const Arguments& arg)
{
return true;
}
// Filter for which functions apply to this suite
static bool function_filter(const Arguments& arg)
{
return !strcmp(arg.function, "check_numerics_matrix");
}
// Google Test name suffix based on parameters
static std::string name_suffix(const Arguments& arg)
{
RocBLAS_TestName<check_numerics_matrix> name(arg.name);
name << rocblas_datatype2string(arg.a_type);
name << '_' << (char)std::toupper(arg.uplo) << '_' << (char)std::toupper(arg.transA)
<< '_' << arg.M << '_' << arg.N;
name << '_' << arg.batch_count;
return std::move(name);
}
};
TEST_P(check_numerics_matrix, auxiliary)
{
CATCH_SIGNALS_AND_EXCEPTIONS_AS_FAILURES(
rocblas_simple_dispatch<check_numerics_matrix_testing>(GetParam()));
}
INSTANTIATE_TEST_CATEGORIES(check_numerics_matrix);
struct check_numerics_matrix_batched
: RocBLAS_Test<check_numerics_matrix, check_numerics_matrix_testing>
{
// Filter for which types apply to this suite
static bool type_filter(const Arguments& arg)
{
return true;
}
// Filter for which functions apply to this suite
static bool function_filter(const Arguments& arg)
{
return !strcmp(arg.function, "check_numerics_matrix_batched");
}
// Google Test name suffix based on parameters
static std::string name_suffix(const Arguments& arg)
{
RocBLAS_TestName<check_numerics_matrix_batched> name(arg.name);
name << rocblas_datatype2string(arg.a_type);
name << '_' << (char)std::toupper(arg.uplo) << '_' << (char)std::toupper(arg.transA)
<< '_' << arg.M << '_' << arg.N;
name << '_' << arg.batch_count;
return std::move(name);
}
};
TEST_P(check_numerics_matrix_batched, auxiliary)
{
CATCH_SIGNALS_AND_EXCEPTIONS_AS_FAILURES(
rocblas_simple_dispatch<check_numerics_matrix_testing>(GetParam()));
}
INSTANTIATE_TEST_CATEGORIES(check_numerics_matrix_batched);
} // namespace