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
535 lines
24 KiB
C++
535 lines
24 KiB
C++
// Copyright (C) 2024 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
|
|
#ifndef TEST_PARAMS_GEN_H
|
|
#define TEST_PARAMS_GEN_H
|
|
|
|
#include <vector>
|
|
|
|
#include "fft_params.h"
|
|
#include "test_params.h"
|
|
|
|
const static std::vector<size_t> batch_range = {2, 1};
|
|
|
|
const static std::vector<fft_precision> precision_range_full
|
|
= {fft_precision_double, fft_precision_single, fft_precision_half};
|
|
const static std::vector<fft_precision> precision_range_sp_dp
|
|
= {fft_precision_double, fft_precision_single};
|
|
|
|
const static std::vector<fft_result_placement> place_range
|
|
= {fft_placement_inplace, fft_placement_notinplace};
|
|
const static std::vector<fft_transform_type> trans_type_range
|
|
= {fft_transform_type_complex_forward, fft_transform_type_real_forward};
|
|
const static std::vector<fft_transform_type> trans_type_range_full
|
|
= {fft_transform_type_complex_forward,
|
|
fft_transform_type_real_forward,
|
|
fft_transform_type_complex_inverse,
|
|
fft_transform_type_real_inverse};
|
|
const static std::vector<fft_transform_type> trans_type_range_complex
|
|
= {fft_transform_type_complex_forward};
|
|
const static std::vector<fft_transform_type> trans_type_range_real
|
|
= {fft_transform_type_real_forward};
|
|
|
|
const static std::vector<fft_callback_type> callbacks_none = {fft_callback_type_none};
|
|
const static std::vector<fft_callback_type> callbacks_full = {fft_callback_type_funcptr};
|
|
|
|
// Take a string (in particular the token from a test) and return a uniform random variable in [0,1]
|
|
// using the seed and hash of the string.
|
|
inline double hash_prob(const int seed, const std::string& token)
|
|
{
|
|
// Keeping the random number generator here
|
|
// allows one to run the same tests for a given
|
|
// random seed; ie the test suite is repeatable.
|
|
std::hash<std::string> hasher;
|
|
std::ranlux24_base gen(seed + hasher(token));
|
|
std::uniform_real_distribution<> dis(0.0, 1.0);
|
|
|
|
const double roll = dis(gen);
|
|
return roll;
|
|
}
|
|
|
|
template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, bool> = true>
|
|
std::vector<T> merge_and_sort_values(const std::vector<std::vector<T>>& set_of_vecs,
|
|
size_t max_num_elem = std::numeric_limits<size_t>::max())
|
|
{
|
|
std::vector<T> merged;
|
|
for(const auto& vec : set_of_vecs)
|
|
{
|
|
std::copy(vec.begin(), vec.end(), std::back_inserter(merged));
|
|
}
|
|
std::sort(merged.begin(), merged.end());
|
|
auto last_unique = std::unique(merged.begin(), merged.end());
|
|
merged.erase(last_unique, merged.end());
|
|
std::ranlux24_base gen(random_seed);
|
|
while(merged.size() > max_num_elem)
|
|
{
|
|
// remove pseudo-randomly chosen elements
|
|
merged.erase(merged.begin() + (static_cast<size_t>(gen()) % merged.size()));
|
|
}
|
|
return merged;
|
|
}
|
|
|
|
// Given a vector of vector of lengths, generate all unique permutations.
|
|
// Add an optional vector of ad-hoc lengths to the result.
|
|
inline std::vector<std::vector<size_t>>
|
|
generate_lengths(const std::vector<std::vector<size_t>>& inlengths)
|
|
{
|
|
std::vector<std::vector<size_t>> output;
|
|
if(inlengths.size() == 0)
|
|
{
|
|
return output;
|
|
}
|
|
const size_t dim = inlengths.size();
|
|
std::vector<size_t> looplength(dim);
|
|
for(unsigned int i = 0; i < dim; ++i)
|
|
{
|
|
looplength[i] = inlengths[i].size();
|
|
}
|
|
for(unsigned int idx = 0; idx < inlengths.size(); ++idx)
|
|
{
|
|
std::vector<size_t> index(dim);
|
|
do
|
|
{
|
|
std::vector<size_t> length(dim);
|
|
for(unsigned int i = 0; i < dim; ++i)
|
|
{
|
|
length[i] = inlengths[i][index[i]];
|
|
}
|
|
output.push_back(length);
|
|
} while(increment_rowmajor(index, looplength));
|
|
}
|
|
// uniquify the result
|
|
std::sort(output.begin(), output.end());
|
|
output.erase(std::unique(output.begin(), output.end()), output.end());
|
|
return output;
|
|
}
|
|
|
|
typedef std::tuple<fft_transform_type, fft_result_placement, fft_array_type, fft_array_type>
|
|
type_place_io_t;
|
|
|
|
// Return the valid rocFFT input and output types for a given transform type.
|
|
inline std::vector<std::pair<fft_array_type, fft_array_type>>
|
|
iotypes(const fft_transform_type transformType,
|
|
const fft_result_placement place,
|
|
const bool planar = true)
|
|
{
|
|
std::vector<std::pair<fft_array_type, fft_array_type>> iotypes;
|
|
switch(transformType)
|
|
{
|
|
case fft_transform_type_complex_forward:
|
|
case fft_transform_type_complex_inverse:
|
|
iotypes.push_back(std::make_pair<fft_array_type, fft_array_type>(
|
|
fft_array_type_complex_interleaved, fft_array_type_complex_interleaved));
|
|
if(planar)
|
|
{
|
|
iotypes.push_back(std::make_pair<fft_array_type, fft_array_type>(
|
|
fft_array_type_complex_planar, fft_array_type_complex_planar));
|
|
if(place == fft_placement_notinplace)
|
|
{
|
|
iotypes.push_back(std::make_pair<fft_array_type, fft_array_type>(
|
|
fft_array_type_complex_planar, fft_array_type_complex_interleaved));
|
|
iotypes.push_back(std::make_pair<fft_array_type, fft_array_type>(
|
|
fft_array_type_complex_interleaved, fft_array_type_complex_planar));
|
|
}
|
|
}
|
|
break;
|
|
case fft_transform_type_real_forward:
|
|
iotypes.push_back(std::make_pair<fft_array_type, fft_array_type>(
|
|
fft_array_type_real, fft_array_type_hermitian_interleaved));
|
|
if(planar && place == fft_placement_notinplace)
|
|
{
|
|
iotypes.push_back(std::make_pair<fft_array_type, fft_array_type>(
|
|
fft_array_type_real, fft_array_type_hermitian_planar));
|
|
}
|
|
break;
|
|
case fft_transform_type_real_inverse:
|
|
iotypes.push_back(std::make_pair<fft_array_type, fft_array_type>(
|
|
fft_array_type_hermitian_interleaved, fft_array_type_real));
|
|
if(planar && place == fft_placement_notinplace)
|
|
{
|
|
iotypes.push_back(std::make_pair<fft_array_type, fft_array_type>(
|
|
fft_array_type_hermitian_planar, fft_array_type_real));
|
|
}
|
|
break;
|
|
default:
|
|
throw std::runtime_error("Invalid transform type");
|
|
}
|
|
return iotypes;
|
|
}
|
|
|
|
// Generate all combinations of input/output types, from combinations of transform and placement
|
|
// types.
|
|
static std::vector<type_place_io_t>
|
|
generate_types(fft_transform_type transform_type,
|
|
const std::vector<fft_result_placement>& place_range,
|
|
const bool planar)
|
|
{
|
|
std::vector<type_place_io_t> ret;
|
|
for(auto place : place_range)
|
|
{
|
|
for(auto iotype : iotypes(transform_type, place, planar))
|
|
{
|
|
ret.push_back(std::make_tuple(transform_type, place, iotype.first, iotype.second));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
struct stride_generator
|
|
{
|
|
struct stride_dist
|
|
{
|
|
stride_dist(const std::vector<size_t>& s, size_t d)
|
|
: stride(s)
|
|
, dist(d)
|
|
{
|
|
}
|
|
std::vector<size_t> stride;
|
|
size_t dist;
|
|
};
|
|
|
|
// NOTE: allow for this ctor to be implicit, so it's less typing for a test writer
|
|
//
|
|
// cppcheck-suppress noExplicitConstructor
|
|
stride_generator(const std::vector<std::vector<size_t>>& stride_list_in)
|
|
: stride_list(stride_list_in)
|
|
{
|
|
}
|
|
virtual std::vector<stride_dist> generate(const std::vector<size_t>& lengths,
|
|
size_t batch) const
|
|
{
|
|
std::vector<stride_dist> ret;
|
|
for(const auto& s : stride_list)
|
|
ret.emplace_back(s, 0);
|
|
return ret;
|
|
}
|
|
std::vector<std::vector<size_t>> stride_list;
|
|
};
|
|
|
|
// Generate strides such that batch is essentially the innermost dimension
|
|
// e.g. given a batch-2 4x3x2 transform which logically looks like:
|
|
//
|
|
// batch0:
|
|
// A B A B
|
|
// A B A B
|
|
// A B A B
|
|
//
|
|
// A B A B
|
|
// A B A B
|
|
// A B A B
|
|
//
|
|
// batch1:
|
|
// A B A B
|
|
// A B A B
|
|
// A B A B
|
|
//
|
|
// A B A B
|
|
// A B A B
|
|
// A B A B
|
|
//
|
|
// we instead do stride-2 4x3x2 transform where first batch is the
|
|
// A's and second batch is the B's.
|
|
struct stride_generator_3D_inner_batch : public stride_generator
|
|
{
|
|
explicit stride_generator_3D_inner_batch(const std::vector<std::vector<size_t>>& stride_list_in)
|
|
: stride_generator(stride_list_in)
|
|
{
|
|
}
|
|
std::vector<stride_dist> generate(const std::vector<size_t>& lengths,
|
|
size_t batch) const override
|
|
{
|
|
std::vector<stride_dist> ret = stride_generator::generate(lengths, batch);
|
|
std::vector<size_t> strides{lengths[1] * lengths[2] * batch, lengths[2] * batch, batch};
|
|
ret.emplace_back(strides, 1);
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
// Create an array of parameters to pass to gtest. Base generator
|
|
// that allows choosing transform type.
|
|
inline auto param_generator_base(const double base_prob,
|
|
const std::vector<fft_transform_type>& type_range,
|
|
const std::vector<std::vector<size_t>>& v_lengths,
|
|
const std::vector<fft_precision>& precisions,
|
|
const std::vector<size_t>& batch_range,
|
|
decltype(generate_types) types_generator,
|
|
const stride_generator& istride,
|
|
const stride_generator& ostride,
|
|
const std::vector<std::vector<size_t>>& ioffset_range,
|
|
const std::vector<std::vector<size_t>>& ooffset_range,
|
|
const std::vector<fft_result_placement>& place_range,
|
|
const bool planar = true,
|
|
const std::vector<fft_callback_type>& callbacks_range
|
|
= callbacks_none,
|
|
const fft_auto_allocation auto_alloc = fft_auto_allocation_default)
|
|
{
|
|
std::vector<fft_params> params;
|
|
|
|
if(base_prob == 0)
|
|
return params;
|
|
|
|
// For any length, we compute double-precision CPU reference
|
|
// for largest batch size first and reuse for smaller batch
|
|
// sizes, then convert to single-precision.
|
|
|
|
for(auto& transform_type : type_range)
|
|
{
|
|
for(const auto& lengths : v_lengths)
|
|
{
|
|
// try to ensure that we are given literal lengths, not
|
|
// something to be passed to generate_lengths
|
|
if(lengths.empty() || lengths.size() > 3)
|
|
{
|
|
continue;
|
|
}
|
|
{
|
|
for(const auto precision : precisions)
|
|
{
|
|
for(const auto batch : batch_range)
|
|
{
|
|
for(const auto& types :
|
|
types_generator(transform_type, place_range, planar))
|
|
{
|
|
for(const auto& istride_dist : istride.generate(lengths, batch))
|
|
{
|
|
for(const auto& ostride_dist : ostride.generate(lengths, batch))
|
|
{
|
|
for(const auto& ioffset : ioffset_range)
|
|
{
|
|
for(const auto& ooffset : ooffset_range)
|
|
{
|
|
for(const auto run_callbacks : callbacks_range)
|
|
{
|
|
fft_params param;
|
|
|
|
param.length = lengths;
|
|
param.istride = istride_dist.stride;
|
|
param.ostride = ostride_dist.stride;
|
|
param.nbatch = batch;
|
|
param.precision = precision;
|
|
param.transform_type = std::get<0>(types);
|
|
param.placement = std::get<1>(types);
|
|
param.idist = istride_dist.dist;
|
|
param.odist = ostride_dist.dist;
|
|
param.itype = std::get<2>(types);
|
|
param.otype = std::get<3>(types);
|
|
param.ioffset = ioffset;
|
|
param.ooffset = ooffset;
|
|
param.auto_allocate = auto_alloc;
|
|
|
|
if(run_callbacks != fft_callback_type_none)
|
|
{
|
|
// add a test if both input and output support callbacks
|
|
if(param.itype != fft_array_type_complex_planar
|
|
&& param.itype
|
|
!= fft_array_type_hermitian_planar
|
|
&& param.otype
|
|
!= fft_array_type_complex_planar
|
|
&& param.otype
|
|
!= fft_array_type_hermitian_planar)
|
|
{
|
|
param.run_callbacks = run_callbacks;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
param.validate();
|
|
|
|
const double roll
|
|
= hash_prob(random_seed, param.token());
|
|
const double run_prob
|
|
= base_prob
|
|
* (param.is_planar()
|
|
? complex_planar_prob_factor
|
|
: 1.0)
|
|
* (param.is_interleaved()
|
|
? complex_interleaved_prob_factor
|
|
: 1.0)
|
|
* (param.is_real() ? real_prob_factor : 1.0)
|
|
* (run_callbacks != fft_callback_type_none
|
|
? callback_prob_factor
|
|
: 1.0);
|
|
|
|
if(roll > run_prob)
|
|
{
|
|
if(verbose > 4)
|
|
{
|
|
std::cout << "Test skipped: (roll=" << roll
|
|
<< " > " << run_prob << ")\n";
|
|
}
|
|
continue;
|
|
}
|
|
if(param.valid(0))
|
|
{
|
|
params.push_back(param);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return params;
|
|
}
|
|
|
|
// Create an array of parameters to pass to gtest. Default generator
|
|
// that picks all transform types.
|
|
inline auto param_generator(const double base_prob,
|
|
const std::vector<std::vector<size_t>>& v_lengths,
|
|
const std::vector<fft_precision>& precision_range,
|
|
const std::vector<size_t>& batch_range,
|
|
const stride_generator& istride,
|
|
const stride_generator& ostride,
|
|
const std::vector<std::vector<size_t>>& ioffset_range,
|
|
const std::vector<std::vector<size_t>>& ooffset_range,
|
|
const std::vector<fft_result_placement>& place_range,
|
|
const bool planar,
|
|
const std::vector<fft_callback_type>& callbacks_range = callbacks_none,
|
|
const fft_auto_allocation auto_alloc = fft_auto_allocation_default)
|
|
{
|
|
return param_generator_base(base_prob,
|
|
trans_type_range,
|
|
v_lengths,
|
|
precision_range,
|
|
batch_range,
|
|
generate_types,
|
|
istride,
|
|
ostride,
|
|
ioffset_range,
|
|
ooffset_range,
|
|
place_range,
|
|
planar,
|
|
callbacks_range,
|
|
auto_alloc);
|
|
}
|
|
|
|
// Create an array of parameters to pass to gtest. Only tests complex-type transforms
|
|
inline auto
|
|
param_generator_complex(const double base_prob,
|
|
const std::vector<std::vector<size_t>>& v_lengths,
|
|
const std::vector<fft_precision>& precision_range,
|
|
const std::vector<size_t>& batch_range,
|
|
const stride_generator& istride,
|
|
const stride_generator& ostride,
|
|
const std::vector<std::vector<size_t>>& ioffset_range,
|
|
const std::vector<std::vector<size_t>>& ooffset_range,
|
|
const std::vector<fft_result_placement>& place_range,
|
|
const bool planar,
|
|
const std::vector<fft_callback_type>& callbacks_range = callbacks_none,
|
|
const fft_auto_allocation auto_alloc = fft_auto_allocation_default)
|
|
{
|
|
return param_generator_base(base_prob,
|
|
trans_type_range_complex,
|
|
v_lengths,
|
|
precision_range,
|
|
batch_range,
|
|
generate_types,
|
|
istride,
|
|
ostride,
|
|
ioffset_range,
|
|
ooffset_range,
|
|
place_range,
|
|
planar,
|
|
callbacks_range,
|
|
auto_alloc);
|
|
}
|
|
|
|
// Create an array of parameters to pass to gtest.
|
|
inline auto param_generator_real(const double base_prob,
|
|
const std::vector<std::vector<size_t>>& v_lengths,
|
|
const std::vector<fft_precision>& precision_range,
|
|
const std::vector<size_t>& batch_range,
|
|
const stride_generator& istride,
|
|
const stride_generator& ostride,
|
|
const std::vector<std::vector<size_t>>& ioffset_range,
|
|
const std::vector<std::vector<size_t>>& ooffset_range,
|
|
const std::vector<fft_result_placement>& place_range,
|
|
const bool planar,
|
|
const std::vector<fft_callback_type>& callbacks_range
|
|
= callbacks_none,
|
|
const fft_auto_allocation auto_alloc = fft_auto_allocation_default)
|
|
{
|
|
return param_generator_base(base_prob,
|
|
trans_type_range_real,
|
|
v_lengths,
|
|
precision_range,
|
|
batch_range,
|
|
generate_types,
|
|
istride,
|
|
ostride,
|
|
ioffset_range,
|
|
ooffset_range,
|
|
place_range,
|
|
planar,
|
|
callbacks_range,
|
|
auto_alloc);
|
|
}
|
|
|
|
template <class Tcontainer>
|
|
auto param_generator_token(const double base_prob, const Tcontainer& tokens)
|
|
{
|
|
std::vector<fft_params> params;
|
|
|
|
if(base_prob == 0)
|
|
return params;
|
|
|
|
for(const auto& token : tokens)
|
|
{
|
|
fft_params param;
|
|
param.from_token(token);
|
|
param.validate();
|
|
if(param.valid())
|
|
{
|
|
const double roll = hash_prob(random_seed, param.token());
|
|
const double run_prob
|
|
= base_prob * (param.is_planar() ? complex_planar_prob_factor : 1.0)
|
|
* (param.is_interleaved() ? complex_interleaved_prob_factor : 1.0)
|
|
* (param.is_real() ? real_prob_factor : 1.0)
|
|
* (param.is_callback() ? callback_prob_factor : 1.0);
|
|
|
|
if(roll > run_prob)
|
|
{
|
|
if(verbose > 4)
|
|
{
|
|
std::cout << "Test skipped: (roll=" << roll << " > " << run_prob << ")\n";
|
|
}
|
|
continue;
|
|
}
|
|
|
|
params.push_back(param);
|
|
}
|
|
else
|
|
{
|
|
std::cout << "invalid token\n";
|
|
}
|
|
}
|
|
return params;
|
|
}
|
|
|
|
#endif
|