diff --git a/src/common/low_precision_transformations/include/low_precision/slice.hpp b/src/common/low_precision_transformations/include/low_precision/slice.hpp new file mode 100644 index 00000000000000..206a07d7bca9c7 --- /dev/null +++ b/src/common/low_precision_transformations/include/low_precision/slice.hpp @@ -0,0 +1,34 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "layer_transformation.hpp" + +namespace ov { +namespace pass { +namespace low_precision { + +/** + * @ingroup ov_transformation_common_api + * @brief SliceTransformation propagates dequantization operations through Slice operation. + * + * For more details about the transformation, refer to + * [SliceTransformation](@ref openvino_docs_OV_UG_lpt_SliceTransformation) page + * in the OpenVINO Developer Guide. + */ +class LP_TRANSFORMATIONS_API SliceTransformation : public LayerTransformation { +public: + OPENVINO_RTTI("SliceTransformation", "0"); + SliceTransformation(const Params& params = Params()); + bool transform(TransformationContext& context, ov::pass::pattern::Matcher& m) override; + bool canBeTransformed(const TransformationContext& context, std::shared_ptr op) const override; + bool isPrecisionPreserved(std::shared_ptr layer) const noexcept override; +}; + +} // namespace low_precision +} // namespace pass +} // namespace ov diff --git a/src/common/low_precision_transformations/src/low_precision.cpp b/src/common/low_precision_transformations/src/low_precision.cpp index e58374ed3e2b1a..52479a5c2dc1fa 100644 --- a/src/common/low_precision_transformations/src/low_precision.cpp +++ b/src/common/low_precision_transformations/src/low_precision.cpp @@ -68,6 +68,7 @@ #include "low_precision/relu.hpp" #include "low_precision/squeeze.hpp" #include "low_precision/subtract.hpp" +#include "low_precision/slice.hpp" #include "low_precision/space_to_batch.hpp" #include "low_precision/split.hpp" #include "low_precision/shuffle_channels.hpp" @@ -267,6 +268,7 @@ bool ov::pass::low_precision::LowPrecision::run_on_model(const std::shared_ptr + +#include "low_precision/slice.hpp" + +#include "itt.hpp" +#include "openvino/util/log.hpp" +#include "openvino/pass/pattern/op/wrap_type.hpp" +#include "openvino/opsets/opset8.hpp" + +#include "low_precision/network_helper.hpp" + +namespace ov { +namespace pass { +namespace low_precision { + +SliceTransformation::SliceTransformation(const Params& params) : LayerTransformation(params) { + MATCHER_SCOPE(SliceTransformation); + auto matcher = ov::pass::pattern::wrap_type(); + + ov::graph_rewrite_callback callback = [this](pattern::Matcher& m) { + auto op = m.get_match_root(); + if (transformation_callback(op)) { + return false; + } + return transform(*context, m); + }; + + auto m = std::make_shared(matcher, matcher_name); + this->register_matcher(m, callback); +} + +bool SliceTransformation::transform(TransformationContext& context, ov::pass::pattern::Matcher& m) { + if (!SliceTransformation::canBeTransformed(context, m.get_match_root())) { + return false; + } + + const auto strided_slice = NetworkHelper::separateInStandaloneBranch(m.get_match_root(), defaultPrecisions); + auto dequantization = NetworkHelper::getDequantization(strided_slice, defaultPrecisions); + const auto newOperation = moveDequantizationAfter(context, strided_slice, NetworkHelper::getDequantization(strided_slice, defaultPrecisions)); + + OPENVINO_DEBUG("LPT: done: ", newOperation); + return true; +} + +bool SliceTransformation::canBeTransformed(const TransformationContext& context, std::shared_ptr operation) const { + if (!LayerTransformation::canBeTransformed(context, operation)) { + return false; + } + + if (!ov::is_type(operation)) { + return false; + } + + const auto dequantization = NetworkHelper::getDequantization(operation); + return dequantization.isPerTensor(); +} + +bool SliceTransformation::isPrecisionPreserved(std::shared_ptr layer) const noexcept { + return true; +} +} // namespace low_precision +} // namespace pass +} // namespace ov diff --git a/src/plugins/intel_cpu/tests/functional/shared_tests_instances/low_precision_transformations/slice_transformation.cpp b/src/plugins/intel_cpu/tests/functional/shared_tests_instances/low_precision_transformations/slice_transformation.cpp new file mode 100644 index 00000000000000..2696d6f14f8ed8 --- /dev/null +++ b/src/plugins/intel_cpu/tests/functional/shared_tests_instances/low_precision_transformations/slice_transformation.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include "low_precision_transformations/slice_transformation.hpp" + + +using namespace LayerTestsDefinitions; + +namespace { +const std::vector netPrecisions = { + ov::element::f32 +}; + +const std::vector trasformationParamValues = { + LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParams() +}; + +const std::vector params = { + { + { + 256ul, + ov::Shape{ 1, 1, 1, 1 }, + { 0.f }, + { 25.5f }, + { 0.f }, + { 12.8f } + }, + { 0 }, // start + { 2147483647 }, // end + { 2 }, // step + { 2 }, // axes + "u8" + }, + { + { + 256ul, + ov::Shape{ 1, 3, 1, 1 }, + { 0.f, 0.f, 0.f }, + { 255.f / 1.f, 255.f / 2.f, 255.f / 3.f }, + { 0.f, 0.f, 0.f }, + { 255.f / 1.f, 255.f / 2.f, 255.f / 3.f } + }, + { 0 }, // start + { 2147483647 }, // end + { 2 }, // step + { 2 }, // axes + "f32" + }, +}; + +INSTANTIATE_TEST_SUITE_P(smoke_LPT, SliceTransformation, + ::testing::Combine( + ::testing::ValuesIn(netPrecisions), + ::testing::Values(ov::PartialShape({ 1, 3, 24, 24 })), + ::testing::Values(ov::test::utils::DEVICE_CPU), + ::testing::ValuesIn(trasformationParamValues), + ::testing::ValuesIn(params)), + SliceTransformation::getTestCaseName); + +} // namespace diff --git a/src/tests/functional/plugin/shared/include/low_precision_transformations/slice_transformation.hpp b/src/tests/functional/plugin/shared/include/low_precision_transformations/slice_transformation.hpp new file mode 100644 index 00000000000000..783f42c6b1967e --- /dev/null +++ b/src/tests/functional/plugin/shared/include/low_precision_transformations/slice_transformation.hpp @@ -0,0 +1,40 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "shared_test_classes/base/low_precision_transformations/layer_transformation.hpp" +#include "ov_lpt_models/common/fake_quantize_on_data.hpp" +#include "ov_lpt_models/common/dequantization_operations.hpp" + +namespace LayerTestsDefinitions { +class SliceTransformationParam { +public: + ov::builder::subgraph::FakeQuantizeOnData fakeQuantize; + std::vector start; + std::vector stop; + std::vector step; + std::vector axes; + std::string expectedPrecision; +}; + +typedef std::tuple< + ov::element::Type, + ov::PartialShape, + std::string, + ov::pass::low_precision::LayerTransformation::Params, + SliceTransformationParam +> SliceTransformationParams; + +class SliceTransformation : + public testing::WithParamInterface, + public LayerTestsUtils::LayerTransformation { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj); + +protected: + void SetUp() override; +}; + +} // namespace LayerTestsDefinitions diff --git a/src/tests/functional/plugin/shared/src/low_precision_transformations/slice_transformation.cpp b/src/tests/functional/plugin/shared/src/low_precision_transformations/slice_transformation.cpp new file mode 100644 index 00000000000000..78dd8d1550f090 --- /dev/null +++ b/src/tests/functional/plugin/shared/src/low_precision_transformations/slice_transformation.cpp @@ -0,0 +1,72 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "low_precision_transformations/slice_transformation.hpp" +#include +#include +#include + +#include "ov_lpt_models/slice.hpp" + +namespace LayerTestsDefinitions { + +inline std::ostream& operator<<(std::ostream& os, const std::vector& values) { + os << "{ "; + for (size_t i = 0; i < values.size(); ++i) { + os << values[i]; + if (i != (values.size() - 1ul)) { + os << ", "; + } + } + os << " }"; + return os; +} + +std::string SliceTransformation::getTestCaseName(const testing::TestParamInfo& obj) { + ov::element::Type netPrecision; + ov::PartialShape inputShape; + std::string targetDevice; + ov::pass::low_precision::LayerTransformation::Params params; + SliceTransformationParam param;; + std::tie(netPrecision, inputShape, targetDevice, params, param) = obj.param; + + std::ostringstream result; + result << get_test_case_name_by_params(netPrecision, inputShape, targetDevice, params) << "_" << + param.fakeQuantize << "_" << + param.start << "_" << + param.stop << "_" << + param.step << "_" << + param.axes; + return result.str(); +} + +void SliceTransformation::SetUp() { + ov::element::Type netPrecision; + ov::PartialShape inputShape; + ov::pass::low_precision::LayerTransformation::Params params; + SliceTransformationParam param; + std::tie(netPrecision, inputShape, targetDevice, params, param) = this->GetParam(); + + init_input_shapes(inputShape); + + function = ov::builder::subgraph::SliceFunction::get( + netPrecision, + inputShape, + param.fakeQuantize, + param.start, + param.stop, + param.step, + param.axes); +} + +TEST_P(SliceTransformation, CompareWithRefImpl) { + SKIP_IF_CURRENT_TEST_IS_DISABLED(); + run(); + + const auto params = std::get<4>(GetParam()); + const auto& actualPrecision = get_runtime_precision_by_type("StridedSlice"); + EXPECT_EQ(actualPrecision, params.expectedPrecision); +}; + +} // namespace LayerTestsDefinitions diff --git a/src/tests/ov_helpers/ov_lpt_models/include/ov_lpt_models/slice.hpp b/src/tests/ov_helpers/ov_lpt_models/include/ov_lpt_models/slice.hpp new file mode 100644 index 00000000000000..0a8d7b0161743b --- /dev/null +++ b/src/tests/ov_helpers/ov_lpt_models/include/ov_lpt_models/slice.hpp @@ -0,0 +1,30 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include "ov_lpt_models/common/builders.hpp" +#include "ov_lpt_models/common/dequantization_operations.hpp" + +namespace ov { +namespace builder { +namespace subgraph { + +class SliceFunction { +public: + static std::shared_ptr get( + const ov::element::Type inputPrecision, + const ov::PartialShape& inputShape, + const ov::builder::subgraph::FakeQuantizeOnData& fakeQuantize, + const std::vector& start, + const std::vector& stop, + const std::vector& step, + const std::vector& axes); +}; + +} // namespace subgraph +} // namespace builder +} // namespace ov diff --git a/src/tests/ov_helpers/ov_lpt_models/src/slice.cpp b/src/tests/ov_helpers/ov_lpt_models/src/slice.cpp new file mode 100644 index 00000000000000..6701c62898fec9 --- /dev/null +++ b/src/tests/ov_helpers/ov_lpt_models/src/slice.cpp @@ -0,0 +1,49 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ov_lpt_models/slice.hpp" +#include "openvino/opsets/opset8.hpp" + +using namespace ov::pass::low_precision; + +namespace ov { +namespace builder { +namespace subgraph { + +std::shared_ptr SliceFunction::get( + const ov::element::Type inputPrecision, + const ov::PartialShape& inputShape, + const ov::builder::subgraph::FakeQuantizeOnData& fakeQuantize, + const std::vector& start, + const std::vector& stop, + const std::vector& step, + const std::vector& axes) { + const auto input = std::make_shared(inputPrecision, inputShape); + input->set_friendly_name("input"); + const auto fqOnData = makeFakeQuantize(input, inputPrecision, fakeQuantize); + + const auto start_constant = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{ start.size() }, start); + start_constant->set_friendly_name("start"); + const auto stop_constant = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{ stop.size() }, stop); + stop_constant->set_friendly_name("stop"); + const auto step_constant = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{ step.size() }, step); + step_constant->set_friendly_name("step"); + const auto axes_constant = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{ axes.size() }, axes); + axes_constant->set_friendly_name("axes "); + + const auto stridedSlice = std::make_shared(fqOnData, start_constant, stop_constant, step_constant, axes_constant); + stridedSlice->set_friendly_name("slice"); + + const auto res = std::make_shared(stridedSlice); + const auto function = std::make_shared( + ov::ResultVector{ res }, + ov::ParameterVector{ input }, + "SliceTransformation"); + + return function; +} + +} // namespace subgraph +} // namespace builder +} // namespace ov