Files
TheRock/rocm-libraries/projects/rocblas/clients/include/norm.hpp
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

504 lines
17 KiB
C++

/* ************************************************************************
* Copyright (C) 2018-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.
*
* ************************************************************************ */
#pragma once
#include "cblas_interface.hpp"
#include "client_utility.hpp"
#include "norm.hpp"
#include "rocblas.h"
#include "rocblas_vector.hpp"
#include <cstdio>
#include <limits>
#include <memory>
/* =====================================================================
Norm check: norm(A-B)/norm(A), evaluate relative error
=================================================================== */
/*!\file
* \brief compares two results (usually, CPU and GPU results); provides Norm check
*/
/* ========================================Norm Check* ==================================================== */
template <typename T>
void m_axpy_64(int64_t N, T* alpha, T* x, int64_t incx, T* y, int64_t incy)
{
int64_t x_offset = incx >= 0 ? 0 : incx * (1 - N);
int64_t y_offset = incy >= 0 ? 0 : incy * (1 - N);
for(int64_t i = 0; i < N; i++)
{
y[y_offset + i * incy] = (*alpha) * x[x_offset + i * incx] + y[y_offset + i * incy];
}
}
/* ============== Norm Check for General Matrix ============= */
/*! \brief compare the norm error of two matrices hCPU & hGPU */
// Real
template <typename T, std::enable_if_t<!rocblas_is_complex<T>, int> = 0>
double norm_check_general(char norm_type, int64_t M, int64_t N, int64_t lda, T* hCPU, T* hGPU)
{
// norm type can be 'O', 'I', 'F', 'o', 'i', 'f' for one, infinity or Frobenius norm
// one norm is max column sum
// infinity norm is max row sum
// Frobenius is l2 norm of matrix entries
host_vector<double> work(std::max(int64_t(1), M));
int64_t incx = 1;
double alpha = -1.0;
size_t size = M * size_t(N); // copying data so lda is M
host_vector<double> hCPU_double(size);
host_vector<double> hGPU_double(size);
for(int64_t i = 0; i < N; i++)
{
int64_t src_col = i * int64_t(lda);
int64_t dst_col = i * int64_t(M);
for(int64_t j = 0; j < M; j++)
{
hCPU_double[size_t(dst_col + j)] = double(hCPU[src_col + j]);
hGPU_double[size_t(dst_col + j)] = double(hGPU[src_col + j]);
}
}
double cpu_norm = ref_lapack_xlange(norm_type, M, N, hCPU_double.data(), M, work.data());
m_axpy_64(size, &alpha, hCPU_double.data(), incx, hGPU_double.data(), incx);
double error
= ref_lapack_xlange(norm_type, M, N, hGPU_double.data(), M, work.data()) / cpu_norm;
return error;
}
// Complex
template <typename T, std::enable_if_t<rocblas_is_complex<T>, int> = 0>
double norm_check_general(char norm_type, int64_t M, int64_t N, int64_t lda, T* hCPU, T* hGPU)
{
// norm type can be O', 'I', 'F', 'o', 'i', 'f' for one, infinity or Frobenius norm
// one norm is max column sum
// infinity norm is max row sum
// Frobenius is l2 norm of matrix entries
host_vector<double> work(std::max(int64_t(1), M));
int64_t incx = 1;
T alpha = -1.0;
int64_t size = N * (int64_t)lda;
double cpu_norm = ref_lapack_xlange(norm_type, M, N, hCPU, lda, work.data());
m_axpy_64(size, &alpha, hCPU, incx, hGPU, incx);
double error = ref_lapack_xlange(norm_type, M, N, hGPU, lda, work.data()) / cpu_norm;
return error;
}
// For BF16 and half, we convert the results to double first
template <typename T,
typename VEC,
std::enable_if_t<std::is_same_v<T, rocblas_half> || std::is_same_v<T, rocblas_bfloat16>,
int> = 0>
double norm_check_general(char norm_type, int64_t M, int64_t N, int64_t lda, VEC&& hCPU, T* hGPU)
{
size_t size = N * (size_t)lda;
host_vector<double> hCPU_double(size);
host_vector<double> hGPU_double(size);
for(int64_t i = 0; i < N; i++)
{
for(int64_t j = 0; j < M; j++)
{
size_t idx = j + i * (size_t)lda;
hCPU_double[idx] = float(hCPU[idx]);
hGPU_double[idx] = float(hGPU[idx]);
}
}
return norm_check_general<double>(norm_type, M, N, lda, hCPU_double, hGPU_double);
}
/* ============== Norm Check for strided_batched case ============= */
template <typename T, template <typename> class VEC, typename T_hpa>
double norm_check_general(char norm_type,
int64_t M,
int64_t N,
int64_t lda,
rocblas_stride stride_a,
VEC<T_hpa>& hCPU,
T* hGPU,
int64_t batch_count)
{
// norm type can be O', 'I', 'F', 'o', 'i', 'f' for one, infinity or Frobenius norm
// one norm is max column sum
// infinity norm is max row sum
// Frobenius is l2 norm of matrix entries
//
// use triangle inequality ||a+b|| <= ||a|| + ||b|| to calculate upper limit for Frobenius norm
// of strided batched matrix
double cumulative_error = 0.0;
for(size_t i = 0; i < batch_count; i++)
{
auto index = i * stride_a;
auto error = norm_check_general(norm_type, M, N, lda, (T_hpa*)hCPU + index, hGPU + index);
if(norm_type == 'F' || norm_type == 'f')
{
cumulative_error += error;
}
else if(norm_type == 'O' || norm_type == 'o' || norm_type == 'I' || norm_type == 'i')
{
cumulative_error = cumulative_error > error ? cumulative_error : error;
}
}
return cumulative_error;
}
template <typename T, typename U>
double norm_check_general(char norm_type, T& hCPU, U& hGPU)
{
// norm type can be O', 'I', 'F', 'o', 'i', 'f' for one, infinity or Frobenius norm
// one norm is max column sum
// infinity norm is max row sum
// Frobenius is l2 norm of matrix entries
//
// use triangle inequality ||a+b|| <= ||a|| + ||b|| to calculate upper limit for Frobenius norm
// of strided batched matrix
int64_t M = hCPU.m();
int64_t N = hCPU.n();
size_t lda = hCPU.lda();
int64_t batch_count = hCPU.batch_count();
double cumulative_error = 0.0;
for(int64_t b = 0; b < batch_count; b++)
{
auto* CPU = hCPU[b];
auto* GPU = hGPU[b];
auto error = norm_check_general(norm_type, M, N, lda, CPU, GPU);
if(norm_type == 'F' || norm_type == 'f')
{
cumulative_error += error;
}
else if(norm_type == 'O' || norm_type == 'o' || norm_type == 'I' || norm_type == 'i')
{
cumulative_error = cumulative_error > error ? cumulative_error : error;
}
}
return cumulative_error;
}
/* ============== Norm Check for batched case ============= */
template <typename T, typename T_hpa>
double norm_check_general(char norm_type,
int64_t M,
int64_t N,
int64_t lda,
host_batch_vector<T_hpa>& hCPU,
host_batch_vector<T>& hGPU,
int64_t batch_count)
{
// norm type can be O', 'I', 'F', 'o', 'i', 'f' for one, infinity or Frobenius norm
// one norm is max column sum
// infinity norm is max row sum
// Frobenius is l2 norm of matrix entries
//
// use triangle inequality ||a+b|| <= ||a|| + ||b|| to calculate upper limit for Frobenius norm
// of strided batched matrix
double cumulative_error = 0.0;
for(int64_t i = 0; i < batch_count; i++)
{
auto index = i;
auto error = norm_check_general<T>(norm_type, M, N, lda, hCPU[index], hGPU[index]);
if(norm_type == 'F' || norm_type == 'f')
{
cumulative_error += error;
}
else if(norm_type == 'O' || norm_type == 'o' || norm_type == 'I' || norm_type == 'i')
{
cumulative_error = cumulative_error > error ? cumulative_error : error;
}
}
return cumulative_error;
}
template <typename T>
double norm_check_general(
char norm_type, int64_t M, int64_t N, int64_t lda, T* hCPU[], T* hGPU[], int64_t batch_count)
{
// norm type can be O', 'I', 'F', 'o', 'i', 'f' for one, infinity or Frobenius norm
// one norm is max column sum
// infinity norm is max row sum
// Frobenius is l2 norm of matrix entries
//
// use triangle inequality ||a+b|| <= ||a|| + ||b|| to calculate upper limit for Frobenius norm
// of strided batched matrix
double cumulative_error = 0.0;
for(int64_t i = 0; i < batch_count; i++)
{
auto index = i;
auto error = norm_check_general<T>(norm_type, M, N, lda, hCPU[index], hGPU[index]);
if(norm_type == 'F' || norm_type == 'f')
{
cumulative_error += error;
}
else if(norm_type == 'O' || norm_type == 'o' || norm_type == 'I' || norm_type == 'i')
{
cumulative_error = cumulative_error > error ? cumulative_error : error;
}
}
return cumulative_error;
}
/* ============== Norm Check for Symmetric Matrix ============= */
/*! \brief compare the norm error of two Hermitian/symmetric matrices hCPU & hGPU */
template <typename T, std::enable_if_t<!rocblas_is_complex<T>, int> = 0, bool HERM = false>
double norm_check_symmetric(char norm_type, char uplo, int64_t N, int64_t lda, T* hCPU, T* hGPU)
{
// norm type can be M', 'I', 'F', 'l': 'F' (Frobenius norm) is used mostly
host_vector<double> work(std::max(int64_t(1), N));
int64_t incx = 1;
double alpha = -1.0;
size_t size = N * (size_t)lda;
host_vector<double> hCPU_double(size);
host_vector<double> hGPU_double(size);
for(int64_t i = 0; i < N; i++)
{
for(int64_t j = 0; j < N; j++)
{
size_t idx = j + i * (size_t)lda;
hCPU_double[idx] = double(hCPU[idx]);
hGPU_double[idx] = double(hGPU[idx]);
}
}
double cpu_norm
= ref_lapack_xlansy<HERM>(norm_type, uplo, N, hCPU_double.data(), lda, work.data());
m_axpy_64(size, &alpha, hCPU_double.data(), incx, hGPU_double.data(), incx);
double error = ref_lapack_xlansy<HERM>(norm_type, uplo, N, hGPU_double.data(), lda, work.data())
/ cpu_norm;
return error;
}
template <typename T, std::enable_if_t<rocblas_is_complex<T>, int> = 0, bool HERM = false>
double norm_check_symmetric(char norm_type, char uplo, int64_t N, int64_t lda, T* hCPU, T* hGPU)
{
// norm type can be M', 'I', 'F', 'l': 'F' (Frobenius norm) is used mostly
host_vector<double> work(std::max(int64_t(1), N));
int64_t incx = 1;
T alpha = -1.0;
size_t size = (size_t)lda * N;
double cpu_norm = ref_lapack_xlansy<HERM>(norm_type, uplo, N, hCPU, lda, work.data());
m_axpy_64(size, &alpha, hCPU, incx, hGPU, incx);
double error = ref_lapack_xlansy<HERM>(norm_type, uplo, N, hGPU, lda, work.data()) / cpu_norm;
return error;
}
template <>
inline double norm_check_symmetric(
char norm_type, char uplo, int64_t N, int64_t lda, rocblas_half* hCPU, rocblas_half* hGPU)
{
size_t size = N * (size_t)lda;
host_vector<double> hCPU_double(size);
host_vector<double> hGPU_double(size);
for(int64_t i = 0; i < N; i++)
{
for(int64_t j = 0; j < N; j++)
{
size_t idx = j + i * (size_t)lda;
hCPU_double[idx] = hCPU[idx];
hGPU_double[idx] = hGPU[idx];
}
}
return norm_check_symmetric(norm_type, uplo, N, lda, hCPU_double.data(), hGPU_double.data());
}
template <typename T, bool HERM = false>
double norm_check_symmetric(
char norm_type, char uplo, int64_t N, int64_t lda, T* hCPU[], T* hGPU[], int64_t batch_count)
{
double cumulative_error = 0.0;
for(int64_t b = 0; b < batch_count; b++)
{
auto error = norm_check_symmetric<T, HERM>(norm_type, uplo, N, lda, hCPU[b], hGPU[b]);
if(norm_type == 'F' || norm_type == 'f')
{
cumulative_error += error;
}
else if(norm_type == 'O' || norm_type == 'o' || norm_type == 'I' || norm_type == 'i')
{
cumulative_error = cumulative_error > error ? cumulative_error : error;
}
}
return cumulative_error;
}
template <typename T, bool HERM = false>
double norm_check_symmetric(char norm_type,
char uplo,
int64_t N,
int64_t lda,
rocblas_stride stridea,
T* hCPU,
T* hGPU,
int64_t batch_count)
{
double cumulative_error = 0.0;
for(int64_t b = 0; b < batch_count; b++)
{
auto error = norm_check_symmetric<T, HERM>(
norm_type, uplo, N, lda, hCPU + b * stridea, hGPU + b * stridea);
if(norm_type == 'F' || norm_type == 'f')
{
cumulative_error += error;
}
else if(norm_type == 'O' || norm_type == 'o' || norm_type == 'I' || norm_type == 'i')
{
cumulative_error = cumulative_error > error ? cumulative_error : error;
}
}
return cumulative_error;
}
template <typename T>
double matrix_norm_1(int64_t M, int64_t N, int64_t lda, T* hA_gold, T* hA)
{
double max_err_scal = 0.0;
double max_err = 0.0;
double err = 0.0;
double err_scal = 0.0;
for(int64_t i = 0; i < N; i++)
{
for(int64_t j = 0; j < M; j++)
{
size_t idx = j + i * (size_t)lda;
err += rocblas_abs((hA_gold[idx] - hA[idx]));
err_scal += rocblas_abs(hA_gold[idx]);
}
max_err_scal = max_err_scal > err_scal ? max_err_scal : err_scal;
max_err = max_err > err ? max_err : err;
}
return max_err / max_err_scal;
}
// overload with different leading dimensions
template <typename T>
double matrix_norm_1(int64_t M, int64_t N, T* hA_gold, int64_t lda_gold, T* hA, int64_t lda)
{
double max_err_scal = 0.0;
double max_err = 0.0;
double err = 0.0;
double err_scal = 0.0;
for(int64_t i = 0; i < N; i++)
{
for(int64_t j = 0; j < M; j++)
{
size_t idxAg = j + i * (size_t)lda_gold;
size_t idxA = j + i * (size_t)lda;
err += rocblas_abs((hA_gold[idxAg] - hA[idxA]));
err_scal += rocblas_abs(hA_gold[idxAg]);
}
max_err_scal = max_err_scal > err_scal ? max_err_scal : err_scal;
max_err = max_err > err ? max_err : err;
}
return max_err / max_err_scal;
}
template <typename T>
double vector_norm_1(int64_t M, int64_t incx, T* hx_gold, T* hx)
{
double max_err_scal = 0.0;
double max_err = 0.0;
int64_t x_offset = incx >= 0 ? 0 : int64_t(incx) * (1 - M);
for(int64_t i = 0; i < M; i++)
{
size_t idx = x_offset + i * (int64_t)incx;
max_err += rocblas_abs((hx_gold[idx] - hx[idx]));
max_err_scal += rocblas_abs(hx_gold[idx]);
}
return max_err / max_err_scal;
}
template <typename T>
double vector_norm_1(int64_t M,
int64_t incx,
const host_batch_vector<T>& hx_gold,
const host_batch_vector<T>& hx)
{
double max_err = 0.0;
for(size_t b = 0; b < hx_gold.batch_count(); b++)
{
max_err = std::max(max_err, vector_norm_1(M, incx, hx_gold[b], hx[b]));
}
return max_err;
}
template <typename T>
double vector_norm_1(int64_t M,
int64_t incx,
const host_strided_batch_vector<T>& hx_gold,
const host_strided_batch_vector<T>& hx)
{
double max_err = 0.0;
for(size_t b = 0; b < hx_gold.batch_count(); b++)
{
max_err = std::max(
max_err, vector_norm_1(M, incx, hx_gold + b * hx_gold.stride(), hx + b * hx.stride()));
}
return max_err;
}