Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "vendor/Catch2"]
path = vendor/Catch2
url = https://github.com/catchorg/Catch2
18 changes: 18 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.15)
project(sandbox LANGUAGES CXX)

# Setup Catch2
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/vendor/Catch2/CMakeLists.txt")
message(FATAL_ERROR "The git submodule vendor/Catch2 is missing.\nTry running `git submodule update --init`.")
endif()

add_subdirectory(vendor/Catch2)

list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")

# Load catch2 cmake module
include(CTest)
include(Catch)

add_subdirectory(tests)

11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,13 @@ int main() {

## Tests

Crude tests for all C++ complex math functions are provided in `/tests/`. Just defined a correct `CXX` and then `make` , `make run`
Testing is implemented with Catch2 and CMake. Catch2 is added as a git submodule inside the `vendor` directory.

Instructions to build and run tests for DPCPP are below:
```
mkdir build
cd build
cmake -DCMAKE_CXX_COMPILER=$CXX_PATH -DCMAKE_CXX_FLAGS=-fsycl ..
make -j 8
ctest
```
38 changes: 22 additions & 16 deletions hello_world.cpp
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
#include <sycl/sycl.hpp>
#include "sycl_ext_complex.hpp"
#include <complex>
#include <cassert>
#include <complex>
#include <sycl/sycl.hpp>

bool almost_equal(std::complex<double> x, std::complex<double> y, int ulp) {
return std::abs(x-y) <= std::numeric_limits<double>::epsilon() * std::abs(x+y) * ulp || std::abs(x-y) < std::numeric_limits<double>::min();
return std::abs(x - y) <=
std::numeric_limits<double>::epsilon() * std::abs(x + y) * ulp ||
std::abs(x - y) < std::numeric_limits<double>::min();
}

int main() {

std::cout << "Is sycl::ext::cplx::complex<double> trivially_copyable? " << std::boolalpha <<std::is_trivially_copyable<sycl::ext::cplx::complex<double>>::value << '\n';
std::cout
<< "Is sycl::ext::cplx::complex<double> trivially_copyable? "
<< std::boolalpha
<< std::is_trivially_copyable<sycl::ext::cplx::complex<double>>::value
<< '\n';

sycl::queue Q(sycl::gpu_selector{});
std::cout << "Running on "
<< Q.get_device().get_info<sycl::info::device::name>()
<< "\n";

std::complex<double> i00{0.2,0.5};
std::complex<double> i01{0.2,0.3};
std::complex<double> cpu_result = std::pow(i00,i01);
auto* gpu_result = sycl::malloc_shared<sycl::ext::cplx::complex<double>>(1,Q);
<< Q.get_device().get_info<sycl::info::device::name>() << "\n";

std::complex<double> i00{0.2, 0.5};
std::complex<double> i01{0.2, 0.3};
std::complex<double> cpu_result = std::pow(i00, i01);
auto *gpu_result =
sycl::malloc_shared<sycl::ext::cplx::complex<double>>(1, Q);
Q.single_task([=]() {
//Using implicit cast from std::complex -> sycl::ext::cplx::complex
sycl::ext::cplx::complex<double> i11{i01};
gpu_result[0] = sycl::ext::cplx::pow<double>(i00, i11);
}).wait();
// Using implicit cast from std::complex -> sycl::ext::cplx::complex
sycl::ext::cplx::complex<double> i11{i01};
gpu_result[0] = sycl::ext::cplx::pow<double>(i00, i11);
}).wait();

std::cout << "cpu_result " << cpu_result << std::endl;
std::cout << "gpu_result " << gpu_result[0] << std::endl;
//Using implicit cast from sycl::ext::cplx::complex -> std::complex
// Using implicit cast from sycl::ext::cplx::complex -> std::complex
assert(almost_equal(cpu_result, gpu_result[0], 1));
return 0;
}
17 changes: 17 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
file(GLOB test_cases CONFIGURE_DEPENDS "*.cpp")

foreach(test_file IN LISTS test_cases)
if(EXISTS "${test_file}")
get_filename_component(exe_name "${test_file}" NAME_WE)

add_executable(${exe_name} ${test_file})
target_include_directories(${exe_name} PUBLIC ../include/)
target_link_libraries(${exe_name} PRIVATE
Catch2::Catch2WithMain
)

catch_discover_tests(${exe_name})
else()
message(FATAL_ERROR "No file named ${test_file}")
endif()
endforeach()
26 changes: 0 additions & 26 deletions tests/Makefile

This file was deleted.

138 changes: 61 additions & 77 deletions tests/abs_complex.cpp
Original file line number Diff line number Diff line change
@@ -1,105 +1,89 @@
#include "test_helper.hpp"

template <typename T> struct test_abs {
bool operator()(sycl::queue &Q, T init_re, T init_im) {
bool pass = true;
////////////////////////////////////////////////////////////////////////////////
// COMPLEX TESTS
////////////////////////////////////////////////////////////////////////////////

auto std_in = init_std_complex(init_re, init_im);
sycl::ext::cplx::complex<T> cplx_input{init_re, init_im};
TEMPLATE_TEST_CASE("Test complex abs", "[abs]", double, float, sycl::half) {
using T = TestType;

T std_out{};
auto *cplx_out = sycl::malloc_shared<T>(1, Q);
sycl::queue Q;

// Test cases
cmplx<T> input = GENERATE(
cmplx<T>{4.42, 2.02}, cmplx<T>{inf_val<T>, 2.02},
cmplx<T>{4.42, inf_val<T>}, cmplx<T>{inf_val<T>, inf_val<T>},
cmplx<T>{nan_val<T>, 2.02}, cmplx<T>{4.42, nan_val<T>},
cmplx<T>{nan_val<T>, nan_val<T>}, cmplx<T>{nan_val<T>, inf_val<T>},
cmplx<T>{inf_val<T>, nan_val<T>});

auto std_in = init_std_complex(input);
sycl::ext::cplx::complex<T> cplx_input{input.re, input.im};

// Get std::complex output
std_out = std::abs(std_in);
T std_out{};
auto *cplx_out = sycl::malloc_shared<T>(1, Q);

// Check cplx::complex output from device
// Get std::complex output
std_out = std::abs(std_in);

// Check cplx::complex output from device
if (is_type_supported<T>(Q)) {
Q.single_task([=]() {
cplx_out[0] = sycl::ext::cplx::abs<T>(cplx_input);
}).wait();

pass &= check_results(cplx_out[0], std_out, /*is_device*/ true);
check_results(cplx_out[0], std_out);
}

// Check cplx::complex output from host
cplx_out[0] = sycl::ext::cplx::abs<T>(cplx_input);
// Check cplx::complex output from host
cplx_out[0] = sycl::ext::cplx::abs<T>(cplx_input);

pass &= check_results(cplx_out[0], std_out, /*is_device*/ false);
check_results(cplx_out[0], std_out);

sycl::free(cplx_out, Q);
sycl::free(cplx_out, Q);
}

return pass;
}
};
////////////////////////////////////////////////////////////////////////////////
// MARRAY<COMPLEX> TESTS
////////////////////////////////////////////////////////////////////////////////

TEMPLATE_TEST_CASE_SIG("Test marray complex abs", "[abs]",
((typename T, std::size_t NumElements), T, NumElements),
(double, 14), (float, 14), (sycl::half, 14)) {
sycl::queue Q;

template <typename T, std::size_t NumElements> struct test_abs_marray {
bool operator()(sycl::queue &Q, test_marray<T, NumElements> init_re,
test_marray<T, NumElements> init_im) {
bool pass = true;
// Test cases
auto init_re = GENERATE(test_marray<T, NumElements>{
1.0, 4.42, -3, 4.0, 2.02, inf_val<T>, inf_val<T>, 2.02, nan_val<T>,
nan_val<T>, inf_val<T>, nan_val<T>, inf_val<T>, nan_val<T>});
auto init_im = GENERATE(test_marray<T, NumElements>{
1.0, 2.02, 3.5, -4.0, inf_val<T>, 4.42, nan_val<T>, 4.42, nan_val<T>,
nan_val<T>, inf_val<T>, nan_val<T>, inf_val<T>, nan_val<T>});

auto std_in = init_std_complex(init_re.get(), init_im.get());
sycl::marray<sycl::ext::cplx::complex<T>, NumElements> cplx_input =
sycl::ext::cplx::make_complex_marray(init_re.get(), init_im.get());
auto std_in = init_std_complex(init_re.get(), init_im.get());
sycl::marray<sycl::ext::cplx::complex<T>, NumElements> cplx_input =
sycl::ext::cplx::make_complex_marray(init_re.get(), init_im.get());

sycl::marray<T, NumElements> std_out{};
auto *cplx_out = sycl::malloc_shared<sycl::marray<T, NumElements>>(1, Q);
sycl::marray<T, NumElements> std_out{};
auto *cplx_out = sycl::malloc_shared<sycl::marray<T, NumElements>>(1, Q);

// Get std::complex output
for (std::size_t i = 0; i < NumElements; ++i)
std_out[i] = std::abs(std_in[i]);
// Get std::complex output
for (std::size_t i = 0; i < NumElements; ++i)
std_out[i] = std::abs(std_in[i]);

// Check cplx::complex output from device
// Check cplx::complex output from device
if (is_type_supported<T>(Q)) {
Q.single_task([=]() {
*cplx_out = sycl::ext::cplx::abs<T>(cplx_input);
}).wait();

pass &= check_results(*cplx_out, std_out, /*is_device*/ true);

// Check cplx::complex output from host
*cplx_out = sycl::ext::cplx::abs<T>(cplx_input);

pass &= check_results(*cplx_out, std_out, /*is_device*/ false);

sycl::free(cplx_out, Q);

return pass;
}
};

int main() {
sycl::queue Q;

bool test_passes = true;
{
test_passes &= test_valid_types<test_abs>(Q, 4.42, 2.02);
check_results(*cplx_out, std_out);

test_passes &= test_valid_types<test_abs>(Q, INFINITY, 2.02);
test_passes &= test_valid_types<test_abs>(Q, 4.42, INFINITY);
test_passes &= test_valid_types<test_abs>(Q, INFINITY, INFINITY);

test_passes &= test_valid_types<test_abs>(Q, NAN, 2.02);
test_passes &= test_valid_types<test_abs>(Q, 4.42, NAN);
test_passes &= test_valid_types<test_abs>(Q, NAN, NAN);

test_passes &= test_valid_types<test_abs>(Q, NAN, INFINITY);
test_passes &= test_valid_types<test_abs>(Q, INFINITY, NAN);
test_passes &= test_valid_types<test_abs>(Q, NAN, INFINITY);
test_passes &= test_valid_types<test_abs>(Q, INFINITY, NAN);
}

// marray tests
{
constexpr size_t m_size = 14;
test_marray<double, m_size> re = {
1.0, 4.42, -3, 4.0, 2.02, INFINITYd, INFINITYd,
2.02, NANd, NANd, INFINITYd, NANd, INFINITYd, NANd};
test_marray<double, m_size> im = {
1.0, 2.02, 3.5, -4.0, INFINITYd, 4.42, NANd,
4.42, NANd, NANd, INFINITYd, NANd, INFINITYd, NANd};
test_passes &= test_valid_types<test_abs_marray, m_size>(Q, re, im);
}
// Check cplx::complex output from host
*cplx_out = sycl::ext::cplx::abs<T>(cplx_input);

if (!test_passes)
std::cerr << "acos complex test fails\n";
check_results(*cplx_out, std_out);

return !test_passes;
sycl::free(cplx_out, Q);
}
Loading