From 2d3289ec976288ab4a5ef4408d81351ca49c66fd Mon Sep 17 00:00:00 2001 From: Katarzyna Mitrus Date: Fri, 25 Oct 2024 17:47:25 +0200 Subject: [PATCH] [STFT][CPU] Add STFT op to CPU and enable by ref (#27137) ### Details: - Enablement of STFT op in CPU by ref impl - Including support for dynamic shapes, and shape related inputs as Parameters ** There is ongoing work to reuse RDFT Executor within STFT CPU impl. Due to number of needed changes, decided to enable by ref first. ### Tickets: - 147161 Related PR: - https://github.com/openvinotoolkit/openvino/pull/27186 --- .../include/stft_shape_inference.hpp | 18 +- src/core/tests/type_prop/stft.cpp | 1 + src/plugins/intel_cpu/src/cpu_types.cpp | 2 + src/plugins/intel_cpu/src/cpu_types.h | 1 + src/plugins/intel_cpu/src/nodes/stft.cpp | 97 ++++++++++ src/plugins/intel_cpu/src/nodes/stft.h | 50 ++++++ src/plugins/intel_cpu/src/nodes_factory.cpp | 2 + .../src/shape_inference/shape_inference.cpp | 2 + .../single_layer_tests/stft.cpp | 72 ++++++++ .../stft_shape_inference_test.cpp | 167 ++++++++++++++++++ .../shared/include/single_op_tests/stft.hpp | 15 ++ .../shared_test_classes/base/utils/ranges.hpp | 1 + .../shared_test_classes/single_op/stft.hpp | 33 ++++ .../src/single_op/stft.cpp | 88 +++++++++ 14 files changed, 544 insertions(+), 5 deletions(-) create mode 100644 src/plugins/intel_cpu/src/nodes/stft.cpp create mode 100644 src/plugins/intel_cpu/src/nodes/stft.h create mode 100644 src/plugins/intel_cpu/tests/functional/shared_tests_instances/single_layer_tests/stft.cpp create mode 100644 src/plugins/intel_cpu/tests/unit/shape_inference_test/stft_shape_inference_test.cpp create mode 100644 src/tests/functional/plugin/shared/include/single_op_tests/stft.hpp create mode 100644 src/tests/functional/shared_test_classes/include/shared_test_classes/single_op/stft.hpp create mode 100644 src/tests/functional/shared_test_classes/src/single_op/stft.cpp diff --git a/src/core/shape_inference/include/stft_shape_inference.hpp b/src/core/shape_inference/include/stft_shape_inference.hpp index 9605a8fa797d8d..c856b4f905b7e6 100644 --- a/src/core/shape_inference/include/stft_shape_inference.hpp +++ b/src/core/shape_inference/include/stft_shape_inference.hpp @@ -16,6 +16,8 @@ std::vector shape_infer(const STFT* op, const std::vector& input_shapes, const ITensorAccessor& ta = make_tensor_accessor()) { using TDim = typename TRShape::value_type; + using TDimVal = typename TDim::value_type; + NODE_VALIDATION_CHECK(op, input_shapes.size() == 4); const auto& signal_shape = input_shapes[0]; @@ -46,15 +48,19 @@ std::vector shape_infer(const STFT* op, if (signal_shape.rank().is_dynamic()) { return {signal_shape}; } else if (!frame_size || !frame_step) { - return {TRShape{signal_shape[0], -1, -1, 2}}; + return {TRShape{signal_shape[0], TDim(ov::util::dim::inf_bound), TDim(ov::util::dim::inf_bound), 2}}; } const auto& frame_size_val = (*frame_size)[0]; const auto& frame_step_val = (*frame_step)[0]; + const bool is_frame_size_in_range = + 0 < frame_size_val && + (signal_shape[1].is_static() ? static_cast(frame_size_val) <= signal_shape[1].get_length() + : frame_size_val <= signal_shape[1].get_interval().get_max_val()); NODE_SHAPE_INFER_CHECK(op, input_shapes, - 0 < frame_size_val && frame_size_val < signal_shape[1].get_interval().get_max_val(), + is_frame_size_in_range, "Provided frame size is ", frame_size_val, " but must be in range [1, ", @@ -68,16 +74,18 @@ std::vector shape_infer(const STFT* op, frame_step_val, " but must be greater than zero."); + const bool is_win_shape_correct = + window_shape.is_dynamic() || (TDimVal{0} < window_shape[0].get_length() && + window_shape[0].get_length() <= static_cast(frame_size_val)); NODE_SHAPE_INFER_CHECK(op, input_shapes, - window_shape.is_dynamic() || - (0 < window_shape[0].get_length() && window_shape[0].get_length() <= frame_size_val), + is_win_shape_correct, "Window input dimension must be in range [1, ", frame_size_val, "]."); const auto& batch_dim = signal_shape[0]; - const TDim frame_size_dim = TDim{frame_size_val}; + const TDim frame_size_dim = static_cast(frame_size_val); const TDim signal_frame_size_diff = signal_shape[1] - frame_size_dim; TDim fft_samples_dim = (frame_size_val / 2) + 1; diff --git a/src/core/tests/type_prop/stft.cpp b/src/core/tests/type_prop/stft.cpp index 60fe9597032714..702b000d2dd560 100644 --- a/src/core/tests/type_prop/stft.cpp +++ b/src/core/tests/type_prop/stft.cpp @@ -104,6 +104,7 @@ INSTANTIATE_TEST_SUITE_P( type_prop_stft_shape, TypePropSTFTTestP, testing::Values( + std::make_tuple(PartialShape{1, 16}, PartialShape{16}, 16, 16, true, PartialShape{1, 9, 1, 2}), std::make_tuple(PartialShape{1, 48}, PartialShape{16}, 16, 16, true, PartialShape{1, 9, 3, 2}), std::make_tuple(PartialShape{1, 48}, PartialShape{16}, 16, 16, false, PartialShape{1, 3, 9, 2}), std::make_tuple(PartialShape{2, 48}, PartialShape{8}, 16, 4, true, PartialShape{2, 9, 9, 2}), diff --git a/src/plugins/intel_cpu/src/cpu_types.cpp b/src/plugins/intel_cpu/src/cpu_types.cpp index 77215baf779426..953f94cb3d5776 100644 --- a/src/plugins/intel_cpu/src/cpu_types.cpp +++ b/src/plugins/intel_cpu/src/cpu_types.cpp @@ -182,6 +182,7 @@ static const TypeToNameMap& get_type_to_name_tbl() { {"IDFT", Type::DFT}, {"RDFT", Type::RDFT}, {"IRDFT", Type::RDFT}, + {"STFT", Type::STFT}, {"Abs", Type::Math}, {"Acos", Type::Math}, {"Acosh", Type::Math}, @@ -342,6 +343,7 @@ std::string NameFromType(const Type type) { CASE(ShuffleChannels); CASE(DFT); CASE(RDFT); + CASE(STFT); CASE(Math); CASE(CTCLoss); CASE(Bucketize); diff --git a/src/plugins/intel_cpu/src/cpu_types.h b/src/plugins/intel_cpu/src/cpu_types.h index 212ff77dcdd09e..c0a2acc3329a9c 100644 --- a/src/plugins/intel_cpu/src/cpu_types.h +++ b/src/plugins/intel_cpu/src/cpu_types.h @@ -89,6 +89,7 @@ enum class Type { ShuffleChannels, DFT, RDFT, + STFT, Math, CTCLoss, Bucketize, diff --git a/src/plugins/intel_cpu/src/nodes/stft.cpp b/src/plugins/intel_cpu/src/nodes/stft.cpp new file mode 100644 index 00000000000000..d03bded6ad5e43 --- /dev/null +++ b/src/plugins/intel_cpu/src/nodes/stft.cpp @@ -0,0 +1,97 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "stft.h" + +#include "openvino/core/type.hpp" +#include "openvino/op/constant.hpp" +#include "openvino/op/stft.hpp" +#include "openvino/reference/stft.hpp" + +namespace ov { +namespace intel_cpu { +namespace node { + +bool STFT::isSupportedOperation(const std::shared_ptr& op, std::string& errorMessage) noexcept { + try { + if (op->get_type_info() != op::v15::STFT::get_type_info_static()) { + errorMessage = "Only STFT operation from the opset15 is supported by the CPU plugin."; + return false; + } + } catch (...) { + return false; + } + return true; +} + +STFT::STFT(const std::shared_ptr& op, const GraphContext::CPtr& context) + : Node(op, context, NgraphShapeInferFactory(op, PortMask(2, 3))) { + std::string errorMessage; + if (!isSupportedOperation(op, errorMessage)) { + THROW_CPU_NODE_ERR(errorMessage); + } + + const auto stft_op = as_type_ptr(op); + m_transpose_frames = stft_op->get_transpose_frames(); + + m_is_frame_size_const = is_type(stft_op->get_input_node_ptr(FRAME_SIZE_IDX)); + m_is_frame_step_const = is_type(stft_op->get_input_node_ptr(FRAME_STEP_IDX)); +} + +void STFT::getSupportedDescriptors() { + if (getParentEdges().size() != 4) { + THROW_CPU_NODE_ERR("STFT has incorrect number of input edges."); + } + if (getChildEdges().empty()) { + THROW_CPU_NODE_ERR("STFT has incorrect number of output edges."); + } +} + +void STFT::initSupportedPrimitiveDescriptors() { + if (!supportedPrimitiveDescriptors.empty()) + return; + + auto dataPrecision = getOriginalInputPrecisionAtPort(DATA_IDX); + if (!one_of(dataPrecision, ov::element::f32)) { + dataPrecision = ov::element::f32; + } + + std::vector configurators({{LayoutType::ncsp, dataPrecision}, + {LayoutType::ncsp, dataPrecision}, + {LayoutType::ncsp, ov::element::i32}, + {LayoutType::ncsp, ov::element::i32}}); + + addSupportedPrimDesc(configurators, {{LayoutType::ncsp, dataPrecision}}, impl_desc_type::ref_any); +} + +bool STFT::needPrepareParams() const { + return false; +} + +bool STFT::created() const { + return getType() == Type::STFT; +} + +void STFT::execute(dnnl::stream strm) { + ov::reference::stft(getSrcDataAtPortAs(DATA_IDX), + getSrcDataAtPortAs(WINDOW_IDX), + getDstDataAtPortAs(0), + ov::Shape{getSrcMemoryAtPort(DATA_IDX)->getStaticDims()}, + ov::Shape{getSrcMemoryAtPort(WINDOW_IDX)->getStaticDims()}, + (getSrcDataAtPortAs(FRAME_SIZE_IDX))[0], + (getSrcDataAtPortAs(FRAME_STEP_IDX))[0], + m_transpose_frames); +} + +void STFT::executeDynamicImpl(dnnl::stream strm) { + execute(strm); +} + +bool STFT::needShapeInfer() const { + return !(m_is_frame_size_const && m_is_frame_step_const) || Node::needShapeInfer(); +} + +} // namespace node +} // namespace intel_cpu +} // namespace ov diff --git a/src/plugins/intel_cpu/src/nodes/stft.h b/src/plugins/intel_cpu/src/nodes/stft.h new file mode 100644 index 00000000000000..7b1684cae4b674 --- /dev/null +++ b/src/plugins/intel_cpu/src/nodes/stft.h @@ -0,0 +1,50 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "node.h" + +namespace ov { +namespace intel_cpu { +namespace node { + +class STFT : public Node { +public: + STFT(const std::shared_ptr& op, const GraphContext::CPtr& context); + + void getSupportedDescriptors() override; + void initSupportedPrimitiveDescriptors() override; + bool created() const override; + static bool isSupportedOperation(const std::shared_ptr& op, std::string& errorMessage) noexcept; + bool needPrepareParams() const override; + + void execute(dnnl::stream strm) override; + void executeDynamicImpl(dnnl::stream strm) override; + bool canBeInPlace() const override { + return false; + } + +protected: + bool needShapeInfer() const override; + +private: + /// STFT params + bool m_transpose_frames = false; + + bool m_is_frame_size_const = false; + bool m_is_frame_step_const = false; + + // Input indices + static constexpr size_t DATA_IDX = 0lu; + static constexpr size_t WINDOW_IDX = 1lu; + static constexpr size_t FRAME_SIZE_IDX = 2lu; + static constexpr size_t FRAME_STEP_IDX = 3lu; +}; + +} // namespace node +} // namespace intel_cpu +} // namespace ov diff --git a/src/plugins/intel_cpu/src/nodes_factory.cpp b/src/plugins/intel_cpu/src/nodes_factory.cpp index 2e7d3b6456d4af..16cf1b974d8561 100644 --- a/src/plugins/intel_cpu/src/nodes_factory.cpp +++ b/src/plugins/intel_cpu/src/nodes_factory.cpp @@ -99,6 +99,7 @@ #include "nodes/space_to_batch.h" #include "nodes/space_to_depth.h" #include "nodes/split.h" +#include "nodes/stft.h" #include "nodes/strided_slice.h" #include "nodes/subgraph.h" #include "nodes/tensoriterator.h" @@ -214,6 +215,7 @@ Node::NodesFactory::NodesFactory() : Factory("NodesFactory") { INTEL_CPU_NODE(RegionYolo, Type::RegionYolo); INTEL_CPU_NODE(DFT, Type::DFT); INTEL_CPU_NODE(RDFT, Type::RDFT); + INTEL_CPU_NODE(STFT, Type::STFT); INTEL_CPU_NODE(ExtractImagePatches, Type::ExtractImagePatches); INTEL_CPU_NODE(Subgraph, Type::Subgraph); INTEL_CPU_NODE(Composite, Type::SubModel); diff --git a/src/plugins/intel_cpu/src/shape_inference/shape_inference.cpp b/src/plugins/intel_cpu/src/shape_inference/shape_inference.cpp index 5db6f97bba8c02..bb2d5e5e84b267 100644 --- a/src/plugins/intel_cpu/src/shape_inference/shape_inference.cpp +++ b/src/plugins/intel_cpu/src/shape_inference/shape_inference.cpp @@ -112,6 +112,7 @@ #include "split_shape_inference.hpp" #include "squeeze_shape_inference.hpp" #include "static_shape.hpp" +#include "stft_shape_inference.hpp" #include "strided_slice_shape_inference.hpp" #include "string_tensor_pack_shape_inference.hpp" #include "string_tensor_unpack_shape_inference.hpp" @@ -414,6 +415,7 @@ const IStaticShapeInferFactory::TRegistry IStaticShapeInferFactory::registry{ _OV_OP_SHAPE_INFER_MASK_REG(op::v15::Col2Im, ShapeInferTA, util::bit::mask(1, 2)), _OV_OP_SHAPE_INFER_MASK_REG(op::v15::ScatterNDUpdate, ShapeInferTA, util::bit::mask()), _OV_OP_SHAPE_INFER_MASK_REG(opset15::SliceScatter, ShapeInferTA, util::bit::mask(2, 3, 4, 5)), + _OV_OP_SHAPE_INFER_MASK_REG(op::v15::STFT, ShapeInferTA, util::bit::mask(2, 3)), // opset14 _OV_OP_SHAPE_INFER_MASK_REG(opset14::Inverse, ShapeInferTA, util::bit::mask()), _OV_OP_SHAPE_INFER_MASK_REG(opset14::MaxPool, ShapeInferPaddingTA, util::bit::mask()), diff --git a/src/plugins/intel_cpu/tests/functional/shared_tests_instances/single_layer_tests/stft.cpp b/src/plugins/intel_cpu/tests/functional/shared_tests_instances/single_layer_tests/stft.cpp new file mode 100644 index 00000000000000..b5fce57fb22f0c --- /dev/null +++ b/src/plugins/intel_cpu/tests/functional/shared_tests_instances/single_layer_tests/stft.cpp @@ -0,0 +1,72 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "single_op_tests/stft.hpp" + +#include + +#include "common_test_utils/test_constants.hpp" + +namespace ov { +namespace test { +using ov::test::STFTLayerTest; + +const std::vector data_type = {ov::element::f32, ov::element::bf16}; +const std::vector step_size_type = {ov::element::i32, ov::element::i64}; + +const std::vector> input_shapes = { + { // Static shapes + {{}, {{1, 128}}}, // 1st input + {{}, {{8}}}, // 2nd input + {{}, {{}}}, // 3rd input + {{}, {{}}} // 4th input + }, + { // Static shapes + {{}, {{2, 226}}}, // 1st input + {{}, {{16}}}, // 2nd input + {{}, {{}}}, // 3rd input + {{}, {{}}} // 4th input + }, + { // Dynamic dims in the first input shape + {{-1, -1}, {{1, 128}, {2, 226}}}, // 1st input + {{}, {{8}}}, // 2nd input + {{}, {{}}}, // 3rd input + {{}, {{}}} // 4th input + }, + { // Dynamic dims in the first and second input shape + {{-1, -1}, {{1, 128}, {2, 226}}}, // 1st input + {{-1}, {{8}, {16}}}, // 2nd input + {{}, {{}}}, // 3rd input + {{}, {{}}} // 4th input + }, + { // Dynamic dims with range in the first and second input shape + {{{2, 4}, {1, 300}}, {{2, 226}, {3, 128}}}, // 1st input + {{{3, 16}}, {{4}, {16}}}, // 2nd input + {{}, {{}}}, // 3rd input + {{}, {{}}} // 4th input + } +}; + +const std::vector frame_size = {16, 24}; +const std::vector step_size = {2, 3, 4}; + +const std::vector transpose_frames = { + false, + true, +}; + +std::vector in_types = {utils::InputLayerType::CONSTANT, utils::InputLayerType::PARAMETER}; + +const auto testCaseStatic = ::testing::Combine(::testing::ValuesIn(input_shapes), + ::testing::ValuesIn(frame_size), + ::testing::ValuesIn(step_size), + ::testing::ValuesIn(transpose_frames), + ::testing::ValuesIn(data_type), + ::testing::ValuesIn(step_size_type), + ::testing::ValuesIn(in_types), + ::testing::Values(ov::test::utils::DEVICE_CPU)); + +INSTANTIATE_TEST_SUITE_P(smoke_STFT_static, STFTLayerTest, testCaseStatic, STFTLayerTest::getTestCaseName); +} // namespace test +} // namespace ov diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/stft_shape_inference_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/stft_shape_inference_test.cpp new file mode 100644 index 00000000000000..4c2ec30d16ee95 --- /dev/null +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/stft_shape_inference_test.cpp @@ -0,0 +1,167 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "stft_shape_inference.hpp" + +#include + +#include "common_test_utils/test_assertions.hpp" +#include "utils.hpp" + +using namespace ov; +using namespace ov::intel_cpu; +using ov::op::v0::Constant; +using ov::op::v0::Parameter; +using testing::HasSubstr; + +class STFTShapeInferenceTest : public OpStaticShapeInferenceTest {}; + +TEST_F(STFTShapeInferenceTest, all_input_as_params) { + const auto data_type = element::f32; + const auto step_size_type = element::i32; + const auto in_signal = std::make_shared(data_type, ov::PartialShape{-1, -1}); + const auto in_window = std::make_shared(data_type, ov::PartialShape{-1}); + const auto in_frame_size = std::make_shared(step_size_type, ov::Shape{}); + const auto in_frame_step = std::make_shared(step_size_type, ov::Shape{}); + const auto op = make_op(in_signal, in_window, in_frame_size, in_frame_step, true); + + std::vector static_input_shapes = {StaticShape{1, 48}, StaticShape{16}, StaticShape{}, StaticShape{}}; + int32_t frame_size = 16; + int32_t frame_step = 16; + + auto const_data = std::unordered_map{{2, {element::i32, Shape{}, &frame_size}}, + {3, {element::i32, Shape{}, &frame_step}}}; + auto acc = make_tensor_accessor(const_data); + auto static_output_shapes = shape_infer(op.get(), static_input_shapes, acc); + ASSERT_EQ(static_output_shapes[0], StaticShape({1, 9, 3, 2})); +} + +TEST_F(STFTShapeInferenceTest, all_input_as_params_equal_dims) { + const auto data_type = element::f32; + const auto step_size_type = element::i32; + const auto in_signal = std::make_shared(data_type, ov::PartialShape{-1, -1}); + const auto in_window = std::make_shared(data_type, ov::PartialShape{-1}); + const auto in_frame_size = std::make_shared(step_size_type, ov::Shape{}); + const auto in_frame_step = std::make_shared(step_size_type, ov::Shape{}); + const auto op = make_op(in_signal, in_window, in_frame_size, in_frame_step, true); + + std::vector static_input_shapes = {StaticShape{1, 16}, StaticShape{16}, StaticShape{}, StaticShape{}}; + int32_t frame_size = 16; + int32_t frame_step = 16; + + auto const_data = std::unordered_map{{2, {element::i32, Shape{}, &frame_size}}, + {3, {element::i32, Shape{}, &frame_step}}}; + auto acc = make_tensor_accessor(const_data); + auto static_output_shapes = shape_infer(op.get(), static_input_shapes, acc); + ASSERT_EQ(static_output_shapes[0], StaticShape({1, 9, 1, 2})); +} + +TEST_F(STFTShapeInferenceTest, frame_inputs_as_const) { + const auto data_type = element::f32; + const auto step_size_type = element::i32; + const auto in_signal = std::make_shared(data_type, ov::PartialShape{-1, -1}); + const auto in_window = std::make_shared(data_type, ov::PartialShape{-1}); + + int32_t frame_size = 16; + int32_t frame_step = 16; + + const auto in_frame_size = std::make_shared(step_size_type, ov::Shape{}, frame_size); + const auto in_frame_step = std::make_shared(step_size_type, ov::Shape{}, frame_step); + const auto op = make_op(in_signal, in_window, in_frame_size, in_frame_step, true); + + std::vector static_input_shapes = {StaticShape{1, 48}, StaticShape{16}, StaticShape{}, StaticShape{}}; + + auto acc = make_tensor_accessor(); + auto static_output_shapes = shape_infer(op.get(), static_input_shapes, acc); + ASSERT_EQ(static_output_shapes[0], StaticShape({1, 9, 3, 2})); +} + +TEST_F(STFTShapeInferenceTest, frame_size_incompatible_value_big) { + const auto data_type = element::f32; + const auto step_size_type = element::i32; + const auto signal = std::make_shared(data_type, PartialShape{-1, -1}); + const auto window = std::make_shared(data_type, PartialShape{-1}); + const auto in_frame_size = std::make_shared(step_size_type, ov::Shape{}); + const auto in_frame_step = std::make_shared(step_size_type, ov::Shape{}); + + const auto op = make_op(signal, window, in_frame_size, in_frame_step, true); + + std::vector static_input_shapes = {StaticShape{1, 48}, StaticShape{16}, StaticShape{}, StaticShape{}}; + int32_t frame_size = 49; + int32_t frame_step = 16; + + auto const_data = std::unordered_map{{2, {element::i32, Shape{}, &frame_size}}, + {3, {element::i32, Shape{}, &frame_step}}}; + auto acc = make_tensor_accessor(const_data); + OV_EXPECT_THROW(std::ignore = shape_infer(op.get(), static_input_shapes, acc), + NodeValidationFailure, + HasSubstr("Provided frame size is 49 but must be in range [1, 48]")); +} + +TEST_F(STFTShapeInferenceTest, frame_size_incompatible_value_small) { + const auto data_type = element::f32; + const auto step_size_type = element::i32; + const auto signal = std::make_shared(data_type, PartialShape{-1, -1}); + const auto window = std::make_shared(data_type, PartialShape{-1}); + const auto in_frame_size = std::make_shared(step_size_type, ov::Shape{}); + const auto in_frame_step = std::make_shared(step_size_type, ov::Shape{}); + + const auto op = make_op(signal, window, in_frame_size, in_frame_step, true); + + std::vector static_input_shapes = {StaticShape{1, 48}, StaticShape{16}, StaticShape{}, StaticShape{}}; + int32_t frame_size = -1; + int32_t frame_step = 16; + + auto const_data = std::unordered_map{{2, {element::i32, Shape{}, &frame_size}}, + {3, {element::i32, Shape{}, &frame_step}}}; + auto acc = make_tensor_accessor(const_data); + OV_EXPECT_THROW(std::ignore = shape_infer(op.get(), static_input_shapes, acc), + NodeValidationFailure, + HasSubstr("Provided frame size is -1 but must be in range [1, 48]")); +} + +TEST_F(STFTShapeInferenceTest, frame_step_incompatible_value) { + const auto data_type = element::f32; + const auto step_size_type = element::i32; + const auto signal = std::make_shared(data_type, PartialShape{-1, -1}); + const auto window = std::make_shared(data_type, PartialShape{-1}); + const auto in_frame_size = std::make_shared(step_size_type, ov::Shape{}); + const auto in_frame_step = std::make_shared(step_size_type, ov::Shape{}); + + const auto op = make_op(signal, window, in_frame_size, in_frame_step, true); + + std::vector static_input_shapes = {StaticShape{1, 48}, StaticShape{16}, StaticShape{}, StaticShape{}}; + int32_t frame_size = 16; + int32_t frame_step = -1; + + auto const_data = std::unordered_map{{2, {element::i32, Shape{}, &frame_size}}, + {3, {element::i32, Shape{}, &frame_step}}}; + auto acc = make_tensor_accessor(const_data); + OV_EXPECT_THROW(std::ignore = shape_infer(op.get(), static_input_shapes, acc), + NodeValidationFailure, + HasSubstr("Provided frame step is -1 but must be greater than zero")); +} + +TEST_F(STFTShapeInferenceTest, window_incompatible_dim_with_frame_size) { + const auto data_type = element::f32; + const auto step_size_type = element::i32; + const auto signal = std::make_shared(data_type, PartialShape{-1, -1}); + const auto window = std::make_shared(data_type, PartialShape{-1}); + const auto in_frame_size = std::make_shared(step_size_type, ov::Shape{}); + const auto in_frame_step = std::make_shared(step_size_type, ov::Shape{}); + + const auto op = make_op(signal, window, in_frame_size, in_frame_step, true); + + std::vector static_input_shapes = {StaticShape{1, 48}, StaticShape{16}, StaticShape{}, StaticShape{}}; + int32_t frame_size = 8; + int32_t frame_step = 4; + + auto const_data = std::unordered_map{{2, {element::i32, Shape{}, &frame_size}}, + {3, {element::i32, Shape{}, &frame_step}}}; + auto acc = make_tensor_accessor(const_data); + + OV_EXPECT_THROW(std::ignore = shape_infer(op.get(), static_input_shapes, acc), + NodeValidationFailure, + HasSubstr("Window input dimension must be in range [1, 8]")); +} diff --git a/src/tests/functional/plugin/shared/include/single_op_tests/stft.hpp b/src/tests/functional/plugin/shared/include/single_op_tests/stft.hpp new file mode 100644 index 00000000000000..8dad38e0cc7d87 --- /dev/null +++ b/src/tests/functional/plugin/shared/include/single_op_tests/stft.hpp @@ -0,0 +1,15 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "shared_test_classes/single_op/stft.hpp" + +namespace ov { +namespace test { +TEST_P(STFTLayerTest, CompareWithRefs) { + run(); +}; +} // namespace test +} // namespace ov diff --git a/src/tests/functional/shared_test_classes/include/shared_test_classes/base/utils/ranges.hpp b/src/tests/functional/shared_test_classes/include/shared_test_classes/base/utils/ranges.hpp index 188b72ecbef530..19de8ce5eb6a79 100644 --- a/src/tests/functional/shared_test_classes/include/shared_test_classes/base/utils/ranges.hpp +++ b/src/tests/functional/shared_test_classes/include/shared_test_classes/base/utils/ranges.hpp @@ -240,6 +240,7 @@ static std::map inputRanges = { {ov::op::v1::BatchToSpace::get_type_info_static(), Range({{0, 15}}, {{0, 8, 32}})}, {ov::op::v15::BitwiseLeftShift::get_type_info_static(), Range({{0, 5}, {0, 4}}, {})}, {ov::op::v15::BitwiseRightShift::get_type_info_static(), Range({{0, 5}, {0, 4}}, {})}, + {ov::op::v15::STFT::get_type_info_static(), Range({{16, 24}, {1, 16}}, {{-100, 100}, {-100, 100}})}, }; class ModelRange { diff --git a/src/tests/functional/shared_test_classes/include/shared_test_classes/single_op/stft.hpp b/src/tests/functional/shared_test_classes/include/shared_test_classes/single_op/stft.hpp new file mode 100644 index 00000000000000..0e165ace364eae --- /dev/null +++ b/src/tests/functional/shared_test_classes/include/shared_test_classes/single_op/stft.hpp @@ -0,0 +1,33 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include + +#include "common_test_utils/test_enums.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" + +namespace ov { +namespace test { +typedef std::tuple, + int64_t, // frame size value + int64_t, // frame step value + bool, // transpose_frames + ov::element::Type, // data type + ov::element::Type, // size/step type + ov::test::utils::InputLayerType, + ov::test::TargetDevice> + STFTParams; + +class STFTLayerTest : public testing::WithParamInterface, virtual public ov::test::SubgraphBaseTest { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj); + +protected: + void SetUp() override; +}; +} // namespace test +} // namespace ov diff --git a/src/tests/functional/shared_test_classes/src/single_op/stft.cpp b/src/tests/functional/shared_test_classes/src/single_op/stft.cpp new file mode 100644 index 00000000000000..2d25b6f386024b --- /dev/null +++ b/src/tests/functional/shared_test_classes/src/single_op/stft.cpp @@ -0,0 +1,88 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "shared_test_classes/single_op/stft.hpp" + +#include "common_test_utils/ov_tensor_utils.hpp" +#include "shared_test_classes/base/utils/ranges.hpp" + +namespace ov { +namespace test { + +std::string STFTLayerTest::getTestCaseName(const testing::TestParamInfo& obj) { + std::ostringstream result; + const std::vector& data_shapes = std::get<0>(obj.param); + const int64_t frame_size = std::get<1>(obj.param); + const int64_t frame_step = std::get<2>(obj.param); + const bool transpose_frames = std::get<3>(obj.param); + const ElementType& data_type = std::get<4>(obj.param); + const ElementType& step_size_type = std::get<5>(obj.param); + const utils::InputLayerType& param_type = std::get<6>(obj.param); + const ov::test::TargetDevice& dev = std::get<7>(obj.param); + + for (size_t s = 0lu; s < 2; s++) { + const auto& shape_item = data_shapes[s]; + result << "IS" << s << "=("; + result << shape_item.first; + result << ")_TS="; + + for (size_t i = 0lu; i < shape_item.second.size(); i++) { + result << "{"; + result << ov::test::utils::vec2str(shape_item.second[i]); + result << "}_"; + } + } + + result << "FrameSize=" << frame_size << "_"; + result << "StepSize=" << frame_step << "_"; + result << "TransposeFrames=" << transpose_frames << "_"; + + result << "ModelType=" << data_type << "_"; + result << "StepSizeType=" << step_size_type << "_"; + result << "IsParameterOnly=" << param_type << "_"; + + result << "Device=" << dev; + return result.str(); +} + +void STFTLayerTest::SetUp() { + std::vector data_shapes; + int64_t frame_size; // frame size value + int64_t frame_step; // frame step value + bool transpose_frames; // transpose_frames + ElementType data_type; // data type + ElementType step_size_type; // size/step type + utils::InputLayerType param_type; + + std::tie(data_shapes, + frame_size, + frame_step, + transpose_frames, + data_type, + step_size_type, + param_type, + targetDevice) = this->GetParam(); + + init_input_shapes(data_shapes); + + const auto in_signal = std::make_shared(data_type, inputDynamicShapes[0]); + const auto in_window = std::make_shared(data_type, inputDynamicShapes[1]); + + if (param_type == utils::InputLayerType::PARAMETER) { + const auto in_frame_size = std::make_shared(step_size_type, ov::Shape{}); + const auto in_frame_step = std::make_shared(step_size_type, ov::Shape{}); + const auto STFT = + std::make_shared(in_signal, in_window, in_frame_size, in_frame_step, transpose_frames); + function = std::make_shared(STFT->outputs(), + ov::ParameterVector{in_signal, in_window, in_frame_size, in_frame_step}); + } else { + const auto in_frame_size = std::make_shared(step_size_type, ov::Shape{}, frame_size); + const auto in_frame_step = std::make_shared(step_size_type, ov::Shape{}, frame_step); + const auto STFT = + std::make_shared(in_signal, in_window, in_frame_size, in_frame_step, transpose_frames); + function = std::make_shared(STFT->outputs(), ov::ParameterVector{in_signal, in_window}); + } +} +} // namespace test +} // namespace ov