Files
TheRock/rocm-libraries/projects/hiptensor/test/utils.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

786 lines
25 KiB
C++

/*******************************************************************************
*
* MIT License
*
* Copyright (C) 2023-2026 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
* copies 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
* IMPLIED, 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 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*******************************************************************************/
#pragma once
#include <ctime>
#include <fstream>
#include <iostream>
#include <iterator>
#include <math.h>
#include <mutex>
#include <numeric>
#include <unordered_map>
#include <vector>
// hiptensor includes
#include <hiptensor/hiptensor.h>
#include <hiptensor/hiptensor_types.h>
#include <hiptensor/internal/hiptensor_utility.hpp>
#include <hiptensor/internal/types.hpp>
#include "data_types.hpp"
#include "device/common.hpp"
#include "hip_resource.hpp"
#include "type_traits.hpp"
#define HIPTENSOR_FREE_DEVICE(ptr) \
if(ptr != nullptr) \
{ \
CHECK_HIP_ERROR(hipFree(ptr)); \
}
#define HIPTENSOR_FREE_HOST(ptr) \
if(ptr != nullptr) \
{ \
CHECK_HIP_ERROR(hipHostFree(ptr)); \
}
inline double getEpsilon(hiptensorComputeDescriptor_t id)
{
auto toDouble = [](auto const& val) { return static_cast<double>(static_cast<float>(val)); };
if(id == HIPTENSOR_COMPUTE_DESC_16F)
{
return std::pow(2, -10); // F16 machine epsilon (std::numeric_limits<_Float16> isn't valid on Windows MSVC)
}
else if(id == HIPTENSOR_COMPUTE_DESC_16BF)
{
return std::pow(2, -7); // BF16 machine epsilon (std::numeric_limits<hip_bfloat16> isn't valid on Windows MSVC)
}
else if(id == HIPTENSOR_COMPUTE_DESC_32F)
{
return toDouble(std::numeric_limits<float>::epsilon());
}
else if(id == HIPTENSOR_COMPUTE_DESC_64F)
{
return toDouble(std::numeric_limits<double>::epsilon());
}
else if(id == HIPTENSOR_COMPUTE_DESC_8U)
{
return 0;
}
else if(id == HIPTENSOR_COMPUTE_DESC_8I)
{
return 0;
}
else if(id == HIPTENSOR_COMPUTE_DESC_32U)
{
return 0;
}
else if(id == HIPTENSOR_COMPUTE_DESC_32I)
{
return 0;
}
else if(id == HIPTENSOR_COMPUTE_DESC_C32F)
{
return toDouble(std::numeric_limits<float>::epsilon());
}
else if(id == HIPTENSOR_COMPUTE_DESC_C64F)
{
return toDouble(std::numeric_limits<double>::epsilon());
}
else
{
#if !NDEBUG
std::cout << "Unhandled hiptensorComputeDescriptor_t: " << id << std::endl;
#endif // !NDEBUG
return 0;
}
}
inline bool isF16Supported()
{
hipDevice_t mHandle;
hipDeviceProp_t mProps;
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
std::string deviceName(mProps.gcnArchName);
return (deviceName.find("gfx908") != std::string::npos)
|| (deviceName.find("gfx90a") != std::string::npos)
|| (deviceName.find("gfx942") != std::string::npos)
|| (deviceName.find("gfx950") != std::string::npos)
|| (deviceName.find("gfx1100") != std::string::npos)
|| (deviceName.find("gfx1101") != std::string::npos)
|| (deviceName.find("gfx1102") != std::string::npos)
|| (deviceName.find("gfx1103") != std::string::npos)
|| (deviceName.find("gfx1150") != std::string::npos)
|| (deviceName.find("gfx1151") != std::string::npos)
|| (deviceName.find("gfx1152") != std::string::npos)
|| (deviceName.find("gfx1153") != std::string::npos)
|| (deviceName.find("gfx1200") != std::string::npos)
|| (deviceName.find("gfx1201") != std::string::npos)
|| (deviceName.find("gfx1250") != std::string::npos);
}
inline bool isF32Supported()
{
hipDevice_t mHandle;
hipDeviceProp_t mProps;
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
std::string deviceName(mProps.gcnArchName);
return (deviceName.find("gfx908") != std::string::npos)
|| (deviceName.find("gfx90a") != std::string::npos)
|| (deviceName.find("gfx942") != std::string::npos)
|| (deviceName.find("gfx950") != std::string::npos)
|| (deviceName.find("gfx1100") != std::string::npos)
|| (deviceName.find("gfx1101") != std::string::npos)
|| (deviceName.find("gfx1102") != std::string::npos)
|| (deviceName.find("gfx1103") != std::string::npos)
|| (deviceName.find("gfx1150") != std::string::npos)
|| (deviceName.find("gfx1151") != std::string::npos)
|| (deviceName.find("gfx1152") != std::string::npos)
|| (deviceName.find("gfx1153") != std::string::npos)
|| (deviceName.find("gfx1200") != std::string::npos)
|| (deviceName.find("gfx1201") != std::string::npos)
|| (deviceName.find("gfx1250") != std::string::npos);
}
inline bool isF64Supported()
{
hipDevice_t mHandle;
hipDeviceProp_t mProps;
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
std::string deviceName(mProps.gcnArchName);
return (deviceName.find("gfx90a") != std::string::npos)
|| (deviceName.find("gfx942") != std::string::npos)
|| (deviceName.find("gfx950") != std::string::npos)
|| (deviceName.find("gfx1250") != std::string::npos);
}
inline bool isF16F16MatrixCoreSupported()
{
hipDevice_t mHandle;
hipDeviceProp_t mProps;
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
std::string deviceName(mProps.gcnArchName);
return (deviceName.find("gfx1100") != std::string::npos)
|| (deviceName.find("gfx1101") != std::string::npos)
|| (deviceName.find("gfx1102") != std::string::npos)
|| (deviceName.find("gfx1103") != std::string::npos)
|| (deviceName.find("gfx1150") != std::string::npos)
|| (deviceName.find("gfx1151") != std::string::npos)
|| (deviceName.find("gfx1152") != std::string::npos)
|| (deviceName.find("gfx1153") != std::string::npos)
|| (deviceName.find("gfx1200") != std::string::npos)
|| (deviceName.find("gfx1201") != std::string::npos)
|| (deviceName.find("gfx1250") != std::string::npos);
}
inline bool isF32F16MatrixCoreSupported()
{
hipDevice_t mHandle;
hipDeviceProp_t mProps;
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
std::string deviceName(mProps.gcnArchName);
return (deviceName.find("gfx908") != std::string::npos)
|| (deviceName.find("gfx90a") != std::string::npos)
|| (deviceName.find("gfx942") != std::string::npos)
|| (deviceName.find("gfx950") != std::string::npos)
|| (deviceName.find("gfx1100") != std::string::npos)
|| (deviceName.find("gfx1101") != std::string::npos)
|| (deviceName.find("gfx1102") != std::string::npos)
|| (deviceName.find("gfx1103") != std::string::npos)
|| (deviceName.find("gfx1150") != std::string::npos)
|| (deviceName.find("gfx1151") != std::string::npos)
|| (deviceName.find("gfx1152") != std::string::npos)
|| (deviceName.find("gfx1153") != std::string::npos)
|| (deviceName.find("gfx1200") != std::string::npos)
|| (deviceName.find("gfx1201") != std::string::npos)
|| (deviceName.find("gfx1250") != std::string::npos);
}
inline bool isF32F32MatrixCoreSupported()
{
hipDevice_t mHandle;
hipDeviceProp_t mProps;
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
std::string deviceName(mProps.gcnArchName);
return (deviceName.find("gfx908") != std::string::npos)
|| (deviceName.find("gfx90a") != std::string::npos)
|| (deviceName.find("gfx942") != std::string::npos)
|| (deviceName.find("gfx950") != std::string::npos)
|| (deviceName.find("gfx1250") != std::string::npos);
}
inline bool isF16F32MatrixCoreSupported()
{
return isF32F32MatrixCoreSupported();
}
inline bool isF64F64MatrixCoreSupported()
{
hipDevice_t mHandle;
hipDeviceProp_t mProps;
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
std::string deviceName(mProps.gcnArchName);
return (deviceName.find("gfx90a") != std::string::npos)
|| (deviceName.find("gfx942") != std::string::npos)
|| (deviceName.find("gfx950") != std::string::npos);
}
inline bool isF64F32MatrixCoreSupported()
{
hipDevice_t mHandle;
hipDeviceProp_t mProps;
CHECK_HIP_ERROR(hipGetDevice(&mHandle));
CHECK_HIP_ERROR(hipGetDeviceProperties(&mProps, mHandle));
std::string deviceName(mProps.gcnArchName);
return (deviceName.find("gfx90a") != std::string::npos)
|| (deviceName.find("gfx942") != std::string::npos)
|| (deviceName.find("gfx950") != std::string::npos)
|| (deviceName.find("gfx1250") != std::string::npos);
}
inline bool isDataType16Bits(hiptensorDataType_t dataType)
{
return (dataType == HIPTENSOR_R_16F || dataType == HIPTENSOR_R_16BF
|| dataType == HIPTENSOR_C_16F || dataType == HIPTENSOR_C_16BF);
}
inline bool isDataType32Bits(hiptensorDataType_t dataType)
{
return (dataType == HIPTENSOR_R_32F || dataType == HIPTENSOR_C_32F);
}
inline bool isDataType64Bits(hiptensorDataType_t dataType)
{
return (dataType == HIPTENSOR_R_64F || dataType == HIPTENSOR_C_64F);
}
inline bool isDataTypeComplex(hiptensorDataType_t dataType)
{
return (dataType == HIPTENSOR_C_32F || dataType == HIPTENSOR_C_64F
|| dataType == HIPTENSOR_C_16F || dataType == HIPTENSOR_C_16BF);
}
template <typename intT1,
class = typename std::enable_if<std::is_integral<intT1>::value>::type,
typename intT2,
class = typename std::enable_if<std::is_integral<intT2>::value>::type>
static constexpr intT1 ceilDiv(const intT1 numerator, const intT2 divisor)
{
return (numerator + divisor - 1) / divisor;
}
template <typename Container>
auto getProduct(const Container& container,
typename Container::value_type init = typename Container::value_type{1})
{
return std::accumulate(std::begin(container),
std::end(container),
init,
std::multiplies<typename Container::value_type>{});
}
// fill kernel for 'elementSize' elements
template <typename DataType>
__host__ static inline void fillLaunchKernel(DataType* data, uint32_t elementSize, uint32_t seed)
{
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceilDiv(elementSize, blockDim.x), 1, 1);
hipLaunchKernelGGL((fillKernel<DataType>), gridDim, blockDim, 0, 0, data, elementSize, seed);
}
// fill kernel for 'elementSize' elements with positive values
template <typename DataType>
__host__ static inline void
fillPositiveValLaunchKernel(DataType* data, uint32_t elementSize, uint32_t seed)
{
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceilDiv(elementSize, blockDim.x), 1, 1);
hipLaunchKernelGGL(
(fillPositiveValKernel<DataType>), gridDim, blockDim, 0, 0, data, elementSize, seed);
}
// fill kernel wrapper for 'elementSize' elements with a specific value
template <typename DataType>
__host__ static inline void
fillValLaunchKernel(DataType* data, uint32_t elementSize, DataType value)
{
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceilDiv(elementSize, blockDim.x), 1, 1);
hipLaunchKernelGGL(
(fillValKernel<DataType>), gridDim, blockDim, 0, 0, data, elementSize, value);
}
template <typename DDataType>
std::pair<bool, double> compareEqual(DDataType const* deviceD,
DDataType const* hostD,
std::size_t elementsD,
hiptensorComputeDescriptor_t computeType,
double tolerance = 0.0)
{
bool retval = true;
double max_relative_error = 0.0;
auto toDouble
= [](DDataType const& val) { return static_cast<double>(static_cast<float>(val)); };
bool isInf = false;
bool isNaN = false;
std::mutex writeMutex;
#pragma omp parallel for
for(int i = 0; i < elementsD; ++i)
{
auto valDevice = deviceD[i];
auto valHost = hostD[i];
auto numerator = fabs(toDouble(valDevice) - toDouble(valHost));
auto divisor = fabs(toDouble(valDevice)) + fabs(toDouble(valHost)) + 1.0;
if(std::isinf(numerator) || std::isinf(divisor))
{
#pragma omp atomic
isInf |= true;
}
else
{
auto relative_error = numerator / divisor;
if(std::isnan(relative_error))
{
#pragma omp atomic
isNaN |= true;
}
else if(relative_error > max_relative_error)
{
const std::lock_guard<std::mutex> guard(writeMutex);
// Double check in case of stall
if(relative_error > max_relative_error)
{
max_relative_error = relative_error;
}
}
}
if(isInf || isNaN)
{
i = elementsD;
}
}
if(tolerance == 0.0)
{
// use the same default tolerance value as CK
if(computeType == HIPTENSOR_COMPUTE_DESC_16BF
|| std::is_same_v<DDataType, hiptensor::bfloat16_t>)
{
const double epsilon = std::pow(2, -7);
tolerance = epsilon * 2;
}
else if(computeType == HIPTENSOR_COMPUTE_DESC_16F
|| std::is_same_v<DDataType, hiptensor::float16_t>)
{
const double epsilon = std::pow(2, -10);
tolerance = epsilon * 2;
}
else
{
tolerance = 1e-5;
}
}
if(isInf)
{
retval = false;
max_relative_error = std::numeric_limits<DDataType>::infinity();
}
else if(isNaN)
{
retval = false;
max_relative_error = std::numeric_limits<DDataType>::signaling_NaN();
}
else if(max_relative_error > tolerance)
{
retval = false;
}
return std::make_pair(retval, max_relative_error);
}
template <typename DDataType>
std::pair<bool, double> compareEqualLaunchKernel(DDataType* deviceD,
DDataType* hostD,
std::size_t elementsD,
hiptensorComputeDescriptor_t computeType,
double tolerance = 0.0)
{
static hiptensor::HipResource::DevicePtrT relativeErrorPtr;
static std::size_t relativeErrorSize = 0;
auto blockDim = dim3(1024, 1, 1);
auto gridDim = dim3(ceilDiv(elementsD, blockDim.x), 1, 1);
double maxRelativeError;
std::size_t requiredErrorSize = elementsD * sizeof(double);
if(requiredErrorSize > relativeErrorSize)
{
hiptensor::HipResource::reallocDevice(relativeErrorPtr, requiredErrorSize);
relativeErrorSize = requiredErrorSize;
}
hipEvent_t syncEvent;
CHECK_HIP_ERROR(hipEventCreate(&syncEvent));
// Calculate the relative error for each element of Tensor D
hipLaunchKernelGGL((compareEqualKernel<DDataType>),
gridDim,
blockDim,
0,
0,
deviceD,
hostD,
static_cast<double*>(relativeErrorPtr.get()),
elementsD);
CHECK_HIP_ERROR(hipEventRecord(syncEvent));
CHECK_HIP_ERROR(hipEventSynchronize(syncEvent));
// Determine the maximum relative error
blockDim = dim3(512, 1, 1);
uint32_t maxElements = 1024;
uint32_t offset = 1;
for(uint32_t i = elementsD; i > 1; i = ceilDiv(i, maxElements))
{
gridDim = dim3(ceilDiv(i, maxElements), 1, 1);
auto elements = i > maxElements ? maxElements : i;
hipLaunchKernelGGL((maxReduceKernel),
gridDim,
blockDim,
0,
0,
static_cast<double*>(relativeErrorPtr.get()),
elements,
offset,
elementsD);
CHECK_HIP_ERROR(hipEventRecord(syncEvent));
CHECK_HIP_ERROR(hipEventSynchronize(syncEvent));
offset = offset * maxElements;
}
CHECK_HIP_ERROR(hipEventDestroy(syncEvent));
CHECK_HIP_ERROR(hipMemcpy(&maxRelativeError,
static_cast<double*>(relativeErrorPtr.get()),
sizeof(double),
hipMemcpyDeviceToHost));
bool retval = true;
bool isNaN = std::isnan(maxRelativeError);
auto toDouble
= [](DDataType const& val) { return static_cast<double>(static_cast<float>(val)); };
if(tolerance == 0.0)
{
// use the same default tolerance value as CK
if(computeType == HIPTENSOR_COMPUTE_DESC_16BF
|| std::is_same_v<DDataType, hiptensor::bfloat16_t>)
{
const double epsilon = std::pow(2, -7);
tolerance = epsilon * 2;
}
else if(computeType == HIPTENSOR_COMPUTE_DESC_16F
|| std::is_same_v<DDataType, hiptensor::float16_t>)
{
const double epsilon = std::pow(2, -10);
tolerance = epsilon * 2;
}
else
{
tolerance = 1e-5;
}
}
if(isNaN)
{
retval = false;
maxRelativeError = std::numeric_limits<DDataType>::signaling_NaN();
}
else if(maxRelativeError > (tolerance))
{
retval = false;
}
return std::make_pair(retval, maxRelativeError);
}
namespace hiptensor
{
template <typename T>
struct has_const_iterator
{
private:
typedef char yes;
typedef struct
{
char array[2];
} no;
template <typename C>
static yes test(typename C::const_iterator*);
template <typename C>
static no test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
typedef T type;
};
template <typename T>
struct has_begin_end
{
template <typename C>
static char (&f(
typename std::enable_if<std::is_same<decltype(static_cast<typename C::const_iterator (
C::*)() const>(&C::begin)),
typename C::const_iterator (C::*)() const>::value,
void>::type*))[1];
template <typename C>
static char (&f(...))[2];
template <typename C>
static char (&g(
typename std::enable_if<std::is_same<decltype(static_cast<typename C::const_iterator (
C::*)() const>(&C::end)),
typename C::const_iterator (C::*)() const>::value,
void>::type*))[1];
template <typename C>
static char (&g(...))[2];
static bool const beg_value = sizeof(f<T>(0)) == 1;
static bool const end_value = sizeof(g<T>(0)) == 1;
};
template <typename T>
struct is_container
: std::integral_constant<bool,
has_const_iterator<T>::value && has_begin_end<T>::beg_value
&& has_begin_end<T>::end_value>
{
};
// Write the content of arrays as value of a CSV field
// do not use ',' since it is field separator in CSV
// Example: arrays = [[1, 2], [3, 4]
// Output: '[[1 2] [3 4]]'
// Main print function
template <typename Container>
std::ostream& printContainerInCsv(Container const& container, std::ostream& outStream)
{
outStream << "[";
for(int i = 0; i < container.size(); i++)
{
if constexpr(is_container<typename Container::value_type>::value)
{
printContainerInCsv(container[i], outStream);
}
else
{
outStream << container[i];
if(i != container.size() - 1)
{
outStream << ' ';
}
}
}
outStream << "] ";
return outStream;
}
// Print a std::vector<std::vector<T>>
template <typename T>
std::ostream& printVectorInCsv(std::vector<std::vector<T>> const& container,
std::ostream& outStream)
{
outStream << "[";
for(const auto& vec : container)
{
outStream << "[";
for(const auto& element : vec)
{
outStream << element;
if(&element != &vec.back())
{
outStream << " ";
}
}
outStream << "]";
if(&vec != &container.back())
{
outStream << " ";
}
}
outStream << "]";
return outStream;
}
}
inline std::ostream& operator<<(std::ostream& os, const hipFloatComplex& fc)
{
std::string seperator = (hipCimagf(fc) >= 0) ? " + " : "";
return os << hipCrealf(fc) << seperator << hipCimagf(fc) << "i";
}
inline std::ostream& operator<<(std::ostream& os, const hipDoubleComplex& dc)
{
std::string seperator = (hipCimag(dc) >= 0) ? " + " : "";
return os << hipCreal(dc) << seperator << hipCimag(dc) << "i";
}
template <typename T>
void hiptensorPrintArrayElements(std::ostream& stream, T* vec, size_t size)
{
int index = 0;
while(index != size)
{
if(index == size - 1)
{
stream << vec[index];
}
else
{
stream << vec[index] << ", ";
}
index++;
}
}
template <typename S>
void hiptensorPrintVectorElements(const std::vector<S>& vec, std::string sep = " ")
{
for(auto& elem : vec)
{
std::cout << elem;
if(&elem != &vec.back())
{
std::cout << sep;
}
}
}
template <typename F>
void hiptensorPrintElementsToFile(std::ofstream& fs, F* output, size_t size, std::string sep = " ")
{
if(!fs.is_open())
{
std::cout << "File not found!\n";
return;
}
for(int i = 0; i < size; i++)
{
if(i == size - 1)
{
fs << static_cast<F>(output[i]);
}
else
{
fs << static_cast<F>(output[i]) << sep;
}
}
return;
}
namespace std
{
static ostream& operator<<(ostream& os, const hiptensorTensorDescriptor_t desc)
{
os << "dim " << desc->mLengths.size() << ", ";
os << "lengths {";
hiptensorPrintVectorElements(desc->mLengths, ", ");
os << "}, ";
os << "strides {";
hiptensorPrintVectorElements(desc->mStrides, ", ");
os << "}";
return os;
}
}
namespace std
{
template <typename T>
ostream& operator<<(ostream& os, const std::vector<T>& vec)
{
os << "[ ";
for(auto i = 0; i < vec.size(); i++)
{
if(i < vec.size() - 1)
{
os << vec[i] << ", ";
}
else
{
os << vec[i];
}
}
os << " ]";
return os;
}
}