|
| 1 | +/* |
| 2 | + * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | + * All rights reserved. |
| 4 | + * |
| 5 | + * This source code is licensed under the BSD-style license found in the |
| 6 | + * LICENSE file in the root directory of this source tree. |
| 7 | + */ |
| 8 | + |
| 9 | +#include <executorch/kernels/test/FunctionHeaderWrapper.h> // Declares the operator |
| 10 | +#include <executorch/kernels/test/TestUtil.h> |
| 11 | +#include <executorch/kernels/test/supported_features.h> |
| 12 | +#include <executorch/runtime/core/exec_aten/exec_aten.h> |
| 13 | +#include <executorch/runtime/core/exec_aten/testing_util/tensor_factory.h> |
| 14 | +#include <executorch/runtime/core/exec_aten/testing_util/tensor_util.h> |
| 15 | + |
| 16 | +#include <gtest/gtest.h> |
| 17 | + |
| 18 | +using executorch::aten::IntArrayRef; |
| 19 | +using executorch::aten::ScalarType; |
| 20 | +using executorch::aten::Tensor; |
| 21 | +using executorch::runtime::testing::TensorFactory; |
| 22 | + |
| 23 | +class OpFftC2rOutTest : public OperatorTest { |
| 24 | + protected: |
| 25 | + Tensor& op_fft_c2r_out( |
| 26 | + const Tensor& in, |
| 27 | + IntArrayRef dim, |
| 28 | + int64_t normalization, |
| 29 | + int64_t last_dim_size, |
| 30 | + Tensor& out) { |
| 31 | + return torch::executor::aten::_fft_c2r_outf( |
| 32 | + context_, in, dim, normalization, last_dim_size, out); |
| 33 | + } |
| 34 | + |
| 35 | + template < |
| 36 | + class CTYPE_OUT, |
| 37 | + executorch::aten::ScalarType DTYPE_OUT, |
| 38 | + bool expect_failure = false> |
| 39 | + void test_dtype(int64_t norm, int64_t dim = 0) { |
| 40 | + TensorFactory<DTYPE_OUT> tf_out; |
| 41 | + constexpr auto DTYPE_IN = executorch::runtime::toComplexType(DTYPE_OUT); |
| 42 | + TensorFactory<DTYPE_IN> tf_in; |
| 43 | + |
| 44 | + using CTYPE_IN = |
| 45 | + typename executorch::runtime::ScalarTypeToCppType<DTYPE_IN>::type; |
| 46 | + |
| 47 | + std::vector<CTYPE_IN> input_data = { |
| 48 | + CTYPE_IN{24, 4}, |
| 49 | + CTYPE_IN{4, -8}, |
| 50 | + CTYPE_IN{0, 4}, |
| 51 | + |
| 52 | + CTYPE_IN{8, -16}, |
| 53 | + CTYPE_IN{-4, 0}, |
| 54 | + CTYPE_IN{0, 32}, |
| 55 | + |
| 56 | + CTYPE_IN{12, 0}, |
| 57 | + CTYPE_IN{0, 4}, |
| 58 | + CTYPE_IN{-8, 4}, |
| 59 | + |
| 60 | + CTYPE_IN{0, 8}, |
| 61 | + CTYPE_IN{-4, 8}, |
| 62 | + CTYPE_IN{8, 0}, |
| 63 | + }; |
| 64 | + |
| 65 | + Tensor in = tf_in.make({4, 3}, input_data); |
| 66 | + Tensor out = tf_out.full({4, 3}, 0); |
| 67 | + |
| 68 | + int64_t last_dim_size = (dim >= 0 && dim < out.dim()) ? out.sizes()[dim] : 0; |
| 69 | + op_fft_c2r_out(in, {dim}, norm, last_dim_size, out); |
| 70 | + |
| 71 | + double norm_factor = 1; |
| 72 | + if (norm == 1) { |
| 73 | + norm_factor = 2; |
| 74 | + } else if (norm == 2) { |
| 75 | + norm_factor = 4; |
| 76 | + } |
| 77 | + std::vector<CTYPE_OUT> expected_data = {52., -4., -8., 44., 4., -56., 20., 12., -8., -20., 4., 72.}; |
| 78 | + for (auto& elem : expected_data) { |
| 79 | + elem /= norm_factor; |
| 80 | + } |
| 81 | + Tensor expected = tf_out.make({4, 3}, expected_data); |
| 82 | + |
| 83 | + if (!expect_failure) { |
| 84 | + EXPECT_TENSOR_CLOSE(out, expected); |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + template <class CTYPE_OUT, executorch::aten::ScalarType DTYPE_OUT> |
| 89 | + void test_dtype_multiple_axes() { |
| 90 | + TensorFactory<DTYPE_OUT> tf_out; |
| 91 | + constexpr auto DTYPE_IN = executorch::runtime::toComplexType(DTYPE_OUT); |
| 92 | + TensorFactory<DTYPE_IN> tf_in; |
| 93 | + |
| 94 | + using CTYPE_IN = |
| 95 | + typename executorch::runtime::ScalarTypeToCppType<DTYPE_IN>::type; |
| 96 | + |
| 97 | + std::vector<CTYPE_IN> input_data = { |
| 98 | + CTYPE_IN{16, 4}, |
| 99 | + CTYPE_IN{4, -8}, |
| 100 | + CTYPE_IN{0, 4}, |
| 101 | + |
| 102 | + CTYPE_IN{8, -16}, |
| 103 | + CTYPE_IN{-4, 0}, |
| 104 | + CTYPE_IN{0, 36}, |
| 105 | + |
| 106 | + CTYPE_IN{32, 0}, |
| 107 | + CTYPE_IN{0, 4}, |
| 108 | + CTYPE_IN{-8, 4}, |
| 109 | + |
| 110 | + CTYPE_IN{0, 8}, |
| 111 | + CTYPE_IN{-4, 8}, |
| 112 | + CTYPE_IN{8, 0}, |
| 113 | + }; |
| 114 | + |
| 115 | + Tensor in = tf_in.make({4, 3}, input_data); |
| 116 | + Tensor out = tf_out.full({4, 4}, 0); |
| 117 | + |
| 118 | + int64_t last_dim_size = out.sizes()[0]; |
| 119 | + std::array<int64_t, 2> dim = {0, 1}; |
| 120 | + op_fft_c2r_out(in, dim, 1, last_dim_size, out); |
| 121 | + |
| 122 | + std::vector<CTYPE_OUT> expected_data = {12., 12., 16., 16., 1., 15., -11., 3., 12., 20., 0., 8., -1., -15., 3., -27.}; |
| 123 | + Tensor expected = tf_out.make({4, 4}, expected_data); |
| 124 | + EXPECT_TENSOR_CLOSE(out, expected); |
| 125 | + } |
| 126 | +}; |
| 127 | + |
| 128 | +TEST_F(OpFftC2rOutTest, AllDtypesSupported) { |
| 129 | +#define TEST_ENTRY(ctype, dtype) \ |
| 130 | + test_dtype<ctype, ScalarType::dtype>(0); \ |
| 131 | + test_dtype<ctype, ScalarType::dtype>(1); \ |
| 132 | + test_dtype<ctype, ScalarType::dtype>(2); |
| 133 | + ET_FORALL_FLOAT_TYPES(TEST_ENTRY); |
| 134 | +#undef TEST_ENTRY |
| 135 | +} |
| 136 | + |
| 137 | +TEST_F(OpFftC2rOutTest, MultipleDims) { |
| 138 | + #define TEST_ENTRY(ctype, dtype) \ |
| 139 | + test_dtype_multiple_axes<ctype, ScalarType::dtype>(); |
| 140 | + ET_FORALL_FLOAT_TYPES(TEST_ENTRY); |
| 141 | + #undef TEST_ENTRY |
| 142 | +} |
| 143 | + |
| 144 | +TEST_F(OpFftC2rOutTest, InvalidNorm) { |
| 145 | + if (torch::executor::testing::SupportedFeatures::get()->is_aten) { |
| 146 | + GTEST_SKIP() << "ATen MKL path does not validate norm"; |
| 147 | + return; |
| 148 | + } |
| 149 | + auto invalid_norm = [this](int64_t norm) { |
| 150 | + test_dtype<float, ScalarType::Float, /* expect_failure = */ true>(norm); |
| 151 | + }; |
| 152 | + ET_EXPECT_KERNEL_FAILURE(context_, invalid_norm(3)); |
| 153 | + ET_EXPECT_KERNEL_FAILURE(context_, invalid_norm(4)); |
| 154 | + ET_EXPECT_KERNEL_FAILURE(context_, invalid_norm(-1)); |
| 155 | + ET_EXPECT_KERNEL_FAILURE(context_, invalid_norm(9999999)); |
| 156 | +} |
| 157 | + |
| 158 | +TEST_F(OpFftC2rOutTest, InvalidDim) { |
| 159 | + if (torch::executor::testing::SupportedFeatures::get()->is_aten) { |
| 160 | + GTEST_SKIP() << "ATen fails UBSAN"; |
| 161 | + return; |
| 162 | + } |
| 163 | + auto negative_dim = [this]() { |
| 164 | + test_dtype<float, ScalarType::Float, /* expect_failure = */ true>(0, -1); |
| 165 | + test_dtype<float, ScalarType::Float, /* expect_failure = */ true>(0, 3); |
| 166 | + test_dtype<float, ScalarType::Float, /* expect_failure = */ true>(0, 9001); |
| 167 | + }; |
| 168 | + ET_EXPECT_KERNEL_FAILURE(context_, negative_dim()); |
| 169 | +} |
0 commit comments