diff --git a/docs/template_plugin/backend/int_executable.cpp b/docs/template_plugin/backend/int_executable.cpp index 4c119438e3c..0628a8371df 100644 --- a/docs/template_plugin/backend/int_executable.cpp +++ b/docs/template_plugin/backend/int_executable.cpp @@ -5,6 +5,7 @@ #include "int_executable.hpp" #include +#include #include "evaluates_map.hpp" #include "ngraph/except.hpp" @@ -87,6 +88,10 @@ bool runtime::interpreter::INTExecutable::call(const vector(op) != nullptr) { @@ -143,8 +148,20 @@ bool runtime::interpreter::INTExecutable::call(const vector(cloned_node)) { + auto variable = var_extension->get_variable(); + if (!variable_context.get_variable_value(variable)) { + auto h_tensor = std::make_shared(cloned_node->get_input_element_type(0), + cloned_node->get_input_shape(0)); + std::vector data(ov::shape_size(cloned_node->get_input_shape(0)), 0); + h_tensor->write(data.data(), data.size() * sizeof(float)); + variable_context.set_variable_value(variable, std::make_shared(h_tensor)); + } + } + // Call evaluate for cloned_node with static shapes - if (!cloned_node->evaluate(op_outputs, op_inputs)) { + if (!cloned_node->evaluate(op_outputs, op_inputs, eval_context)) { evaluate_node(cloned_node, op_outputs, op_inputs); } if (m_performance_counters_enabled) { diff --git a/docs/template_plugin/backend/opset_int_tbl.hpp b/docs/template_plugin/backend/opset_int_tbl.hpp index 287bf9a0d11..4d4383da233 100644 --- a/docs/template_plugin/backend/opset_int_tbl.hpp +++ b/docs/template_plugin/backend/opset_int_tbl.hpp @@ -85,6 +85,7 @@ NGRAPH_OP(NonMaxSuppression, op::v5) NGRAPH_OP(RNNSequence, op::v5) NGRAPH_OP(Round, op::v5) +NGRAPH_OP(Assign, ngraph::op::v6) NGRAPH_OP(CTCGreedyDecoderSeqLen, op::v6) NGRAPH_OP(ExperimentalDetectronDetectionOutput, op::v6) NGRAPH_OP(ExperimentalDetectronGenerateProposalsSingleImage, op::v6) @@ -93,6 +94,7 @@ NGRAPH_OP(ExperimentalDetectronROIFeatureExtractor, op::v6) NGRAPH_OP(ExperimentalDetectronTopKROIs, op::v6) NGRAPH_OP(GatherElements, op::v6) NGRAPH_OP(MVN, ngraph::op::v6) +NGRAPH_OP(ReadValue, ngraph::op::v6) NGRAPH_OP(DFT, op::v7) NGRAPH_OP(Einsum, op::v7) diff --git a/src/common/low_precision_transformations/include/low_precision/assign_and_read_value.hpp b/src/common/low_precision_transformations/include/low_precision/assign_and_read_value.hpp new file mode 100644 index 00000000000..b7ea91d099e --- /dev/null +++ b/src/common/low_precision_transformations/include/low_precision/assign_and_read_value.hpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include "layer_transformation.hpp" + +namespace ngraph { +namespace pass { +namespace low_precision { + +class LP_TRANSFORMATIONS_API AssignAndReadValueTransformation : public LayerTransformation { +public: + NGRAPH_RTTI_DECLARATION; + AssignAndReadValueTransformation(const std::shared_ptr function, const Params& params = Params()); + bool transform(TransformationContext& context, ngraph::pattern::Matcher& m) override; + bool canBeTransformed(const TransformationContext& context, std::shared_ptr op) const override; + bool isPrecisionPreserved(std::shared_ptr layer) const noexcept override; +private: + std::shared_ptr function; +}; + +} // namespace low_precision +} // namespace pass +} // namespace ngraph \ No newline at end of file diff --git a/src/common/low_precision_transformations/include/low_precision/common/fake_quantize_dequantization.hpp b/src/common/low_precision_transformations/include/low_precision/common/fake_quantize_dequantization.hpp index 0da82810c97..4bc50744c5a 100644 --- a/src/common/low_precision_transformations/include/low_precision/common/fake_quantize_dequantization.hpp +++ b/src/common/low_precision_transformations/include/low_precision/common/fake_quantize_dequantization.hpp @@ -33,6 +33,7 @@ public: bool multiplyHasZeroOrDenormal() const; bool isShared() const; bool isLowPrecision() const; + std::shared_ptr copyWithNewInput(const std::shared_ptr& input) const; static bool checkElementwise(const std::shared_ptr& elementwise); diff --git a/src/common/low_precision_transformations/include/low_precision/fake_quantize.hpp b/src/common/low_precision_transformations/include/low_precision/fake_quantize.hpp index 15975782ef0..6a3f84b6b4c 100644 --- a/src/common/low_precision_transformations/include/low_precision/fake_quantize.hpp +++ b/src/common/low_precision_transformations/include/low_precision/fake_quantize.hpp @@ -22,11 +22,10 @@ public: static bool checkElementwise(const std::shared_ptr& eltwise); -private: - std::shared_ptr fuseElementwise( + static std::shared_ptr fuseElementwise( TransformationContext& context, MatcherPass* matcherPass, - const std::shared_ptr& fakeQuantize) const; + const std::shared_ptr& fakeQuantize); }; } // namespace low_precision diff --git a/src/common/low_precision_transformations/include/low_precision/network_helper.hpp b/src/common/low_precision_transformations/include/low_precision/network_helper.hpp index 5f7d6a74113..569cd79d506 100644 --- a/src/common/low_precision_transformations/include/low_precision/network_helper.hpp +++ b/src/common/low_precision_transformations/include/low_precision/network_helper.hpp @@ -41,17 +41,12 @@ public: static std::vector> consumer_inputs(std::shared_ptr node); - // returns true if at least one child is not FQ - static bool notAllChildrensAreFQ(const NodeVector& layer); - // Collect and return a vector with all nodes that consumes any of the `node` output static std::vector> consumers(std::shared_ptr node); // return true if op is on a constant path static bool isConstantPath(const std::shared_ptr& op); - static Shape alignShapeForChannelDim(const Shape& shape, Rank rank); - template static std::shared_ptr setOutDataPrecisionForTypeRelaxed(std::shared_ptr operation, const element::Type& precision); @@ -215,87 +210,6 @@ public: const std::shared_ptr& dequantization, const std::shared_ptr& newNode); - static void replaceAttributeInNodes( - std::shared_ptr f, - const std::string& name, - const ov::Any& newAttribute, - const ov::Any& oldAttribute, - const std::shared_ptr& initialNode) { - std::set> visited; - std::deque> nodes; - nodes.emplace_back(initialNode); - - while (!nodes.empty()) { - auto node = nodes.front(); - nodes.pop_front(); - - if (visited.count(node) || ov::is_type(node)) { - continue; - } - - visited.insert(node); - - bool handleConnectedNodes = false; - if (NetworkHelper::isPrecisionPreserved(node) || ov::is_type(node)) { - auto& rt = node->get_rt_info(); - - if (node == initialNode) { - rt[name] = newAttribute; - handleConnectedNodes = true; - } else { - auto it = rt.find(name); - if (it != rt.end()) { - const auto currentAttribute = it->second; - if (oldAttribute == currentAttribute) { - rt[name] = newAttribute; - } - handleConnectedNodes = true; - } - } - } - - if (!handleConnectedNodes) { - continue; - } - - if (!ov::is_type(node)) { - for (size_t index = 0ul; index < node->get_input_size(); ++index) { - auto getInput = [](const std::shared_ptr& node, const size_t index) { - const auto dequantization = NetworkHelper::getDequantization(node, index); - if (!dequantization.empty() && - (ov::is_type(dequantization.data.get_node())) && - ov::is_type(dequantization.data.get_node()->get_input_node_ptr(0))) { - const auto input = dequantization.data.get_node()->input(0); - return input; - } - return node->input(index); - }; - - const auto& input = getInput(node, index); - const auto& input_node = input.get_source_output().get_node_shared_ptr(); - - //const auto& input_node = input.get_source_output().get_node_shared_ptr(); - if (visited.count(input_node) || ov::is_type(input_node)) { - continue; - } - - nodes.push_front(input_node); - } - } - - for (auto& output : node->outputs()) { - for (auto& input_value : output.get_target_inputs()) { - const auto& output_node = input_value.get_node()->shared_from_this(); - if (visited.count(output_node) || ov::is_type(output_node)) { - continue; - } - - nodes.push_front(output_node); - } - } - } - } - template static void reassign( const std::shared_ptr& sharedValue, @@ -370,14 +284,6 @@ std::shared_ptr make_op_pattern(const ngraph::NodeVector& args) { return std::make_shared(element::undefined, PartialShape{}, [](std::shared_ptr n) {return !!ov::as_type_ptr(n); }, args); } -template -std::shared_ptr make_op_label() { - return std::make_shared( - element::undefined, - PartialShape{}, - [](std::shared_ptr n) {return !!ov::as_type_ptr(n); }); -} - template std::shared_ptr fold(Args&&... args) { auto node = std::make_shared(std::forward(args)...); diff --git a/src/common/low_precision_transformations/src/assign_and_read_value.cpp b/src/common/low_precision_transformations/src/assign_and_read_value.cpp new file mode 100644 index 00000000000..472b8df8eb2 --- /dev/null +++ b/src/common/low_precision_transformations/src/assign_and_read_value.cpp @@ -0,0 +1,132 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "low_precision/assign_and_read_value.hpp" +#include + +#include +#include "low_precision/network_helper.hpp" +#include +#include +#include +#include "low_precision/fake_quantize.hpp" + +namespace ngraph { +namespace pass { +namespace low_precision { + +NGRAPH_RTTI_DEFINITION(ngraph::pass::low_precision::AssignAndReadValueTransformation, "AssignAndReadValueTransformation", 0); + +AssignAndReadValueTransformation::AssignAndReadValueTransformation(const std::shared_ptr function, const Params& params) : + LayerTransformation(params), function(function) { + auto assign3 = pattern::wrap_type({ pattern::wrap_type() }); + auto assign6 = pattern::wrap_type({ pattern::wrap_type() }); + + ngraph::graph_rewrite_callback callback = [=](pattern::Matcher& m) { + const auto& opsMap = m.get_pattern_value_map(); + auto op = m.get_match_root(); + auto assignIt = opsMap.find(assign3); + if (assignIt == opsMap.end()) { + assignIt = opsMap.find(assign6); + } + const auto assign = assignIt->second.get_node_shared_ptr(); + // check that we have ReadValue as the first dependency + if (assign->get_control_dependencies().empty()) { + return false; + } + + if (transformation_callback(op)) { + return false; + } + return transform(*context, m); + }; + + auto m = std::make_shared( + std::make_shared(OutputVector{ assign3, assign6 }), + "AssignAndReadValueTransformation"); + this->register_matcher(m, callback); +} + +bool AssignAndReadValueTransformation::transform(TransformationContext& context, ngraph::pattern::Matcher& m) { + if (!canBeTransformed(context, m.get_match_root())) { + return false; + } + + const auto oldAssign = m.get_match_root(); + const auto readValue = oldAssign->get_control_dependencies()[0]; + oldAssign->remove_control_dependency(readValue); + + const auto assign = NetworkHelper::separateInStandaloneBranch(oldAssign); + const auto dequantization = NetworkHelper::getDequantization(assign); + + auto oldVar = ov::as_type_ptr(readValue)->get_variable(); + auto variableInfo = oldVar->get_info(); + // set new precision for oldVar to update precision in newReadValue + oldVar->update({variableInfo.data_shape, dequantization.data.get_element_type(), variableInfo.variable_id}); + // transform ReadValue part + const auto newConstant = foldConvert(readValue->get_input_node_shared_ptr(0), dequantization.data.get_element_type()); + const auto newReadValue = readValue->copy_with_new_inputs({newConstant}); + const auto newDequantization = dequantization.copyWithNewInput(newReadValue); + replace_node(readValue, newDequantization); + + // transform Assign part + + const auto newAssign = assign->copy_with_new_inputs({dequantization.data}); + function->remove_sink(as_type_ptr(oldAssign)); + function->add_sinks({as_type_ptr(newAssign)}); + + NetworkHelper::copyInfo(assign, newAssign); + replace_node(assign, newAssign); + newAssign->add_control_dependency(newReadValue); + + // fuse dequantization multiply with FQ after ReadValue if possible + const auto nextLayers = newDequantization->get_output_target_inputs(0); + if (nextLayers.size() > 1) { + return true; + } + const auto fakeQuantize = as_type_ptr(nextLayers.begin()->get_node()->shared_from_this()); + + if (fakeQuantize == nullptr) { + return true; + } + auto fakeQuantizeInputs = fakeQuantize->input_values(); + + const auto inputLow = as_type_ptr(fakeQuantizeInputs[1].get_node_shared_ptr()); + const auto inputHigh = as_type_ptr(fakeQuantizeInputs[2].get_node_shared_ptr()); + + if (inputLow == nullptr || inputHigh == nullptr) { + return true; + } + + FakeQuantizeTransformation::fuseElementwise(context, this, fakeQuantize); + + return true; +} + +bool AssignAndReadValueTransformation::canBeTransformed(const TransformationContext& context, std::shared_ptr op) const { + if (!LayerTransformation::canBeTransformed(context, op)) { + return false; + } + + const auto readValue = std::dynamic_pointer_cast(op->get_control_dependencies()[0]); + if (!readValue) { + return false; + } + + // TODO: remove this limitation and change the transformation when this constant will be accepted to be non-zero + if (!NetworkHelper::isZeroConst(readValue->get_input_node_shared_ptr(0))) { + return false; + } + + const auto dequantization = NetworkHelper::getDequantization(op); + return dequantization.subtract == nullptr && dequantization.multiply != nullptr; +} + +bool AssignAndReadValueTransformation::isPrecisionPreserved(std::shared_ptr layer) const noexcept { + return false; +} + +} // namespace low_precision +} // namespace pass +} // namespace ngraph diff --git a/src/common/low_precision_transformations/src/fake_quantize.cpp b/src/common/low_precision_transformations/src/fake_quantize.cpp index deb7ec1ea28..130ab6f5103 100644 --- a/src/common/low_precision_transformations/src/fake_quantize.cpp +++ b/src/common/low_precision_transformations/src/fake_quantize.cpp @@ -129,17 +129,15 @@ bool FakeQuantizeTransformation::checkElementwise(const std::shared_ptr& e std::shared_ptr FakeQuantizeTransformation::fuseElementwise( TransformationContext& context, MatcherPass* matcherPass, - const std::shared_ptr& fakeQuantize) const { + const std::shared_ptr& fakeQuantize) { const std::shared_ptr eltwise = fakeQuantize->get_input_node_shared_ptr(0); - std::shared_ptr inputLowConst_f32 = foldConvert(fakeQuantize->input_value(1), deqPrecision); - std::shared_ptr inputHighConst_f32 = foldConvert(fakeQuantize->input_value(2), deqPrecision); + std::shared_ptr inputLowConst_f32 = foldConvert(fakeQuantize->input_value(1), element::f32); + std::shared_ptr inputHighConst_f32 = foldConvert(fakeQuantize->input_value(2), element::f32); std::shared_ptr constant = fq::getConstant(eltwise); if (ov::is_type(eltwise) && checkElementwise(eltwise)) { - const auto value = constant->get_output_element_type(0) == deqPrecision ? - constant : - foldConvert(constant, deqPrecision); + const auto value = foldConvert(constant, element::f32); const auto valueVec = ov::as_type_ptr(value)->cast_vector(); @@ -159,9 +157,7 @@ std::shared_ptr FakeQuantizeTransformation::fuseElementwis inputLowConst_f32 = fq::updateShape(inputLowConst_f32, fakeQuantize->get_output_partial_shape(0)); inputHighConst_f32 = fq::updateShape(inputHighConst_f32, fakeQuantize->get_output_partial_shape(0)); } else if (ov::is_type(eltwise) && checkElementwise(eltwise)) { - const auto value = constant->get_output_element_type(0) == deqPrecision ? - constant : - foldConvert(constant, deqPrecision); + const auto value = foldConvert(constant, element::f32); inputLowConst_f32 = fq::updateShape(fold(inputLowConst_f32, value), fakeQuantize->get_output_partial_shape(0)); inputHighConst_f32 = fq::updateShape(fold(inputHighConst_f32, value), fakeQuantize->get_output_partial_shape(0)); @@ -173,9 +169,7 @@ std::shared_ptr FakeQuantizeTransformation::fuseElementwis return nullptr; } - const auto value = constant->get_output_element_type(0) == deqPrecision ? - constant : - foldConvert(constant, deqPrecision); + const auto value = foldConvert(constant, element::f32); inputLowConst_f32 = fq::updateShape(fold(inputLowConst_f32, value), fakeQuantize->get_output_partial_shape(0)); inputHighConst_f32 = fq::updateShape(fold(inputHighConst_f32, value), fakeQuantize->get_output_partial_shape(0)); @@ -196,8 +190,8 @@ std::shared_ptr FakeQuantizeTransformation::fuseElementwis data->output(outputIdx), inputLowConst_f32, inputHighConst_f32, - foldConvert(fakeQuantize->input_value(3), deqPrecision), - foldConvert(fakeQuantize->input_value(4), deqPrecision) })); + foldConvert(fakeQuantize->input_value(3), element::f32), + foldConvert(fakeQuantize->input_value(4), element::f32) })); matcherPass->register_new_node(newFakeQuantize); diff --git a/src/common/low_precision_transformations/src/fake_quantize_dequantization.cpp b/src/common/low_precision_transformations/src/fake_quantize_dequantization.cpp index 46b56716081..fc6ae961a24 100644 --- a/src/common/low_precision_transformations/src/fake_quantize_dequantization.cpp +++ b/src/common/low_precision_transformations/src/fake_quantize_dequantization.cpp @@ -155,6 +155,26 @@ bool FakeQuantizeDequantization::checkElementwise(const std::shared_ptr FakeQuantizeDequantization::copyWithNewInput(const std::shared_ptr& input) const { + auto lastNode = input; + if (convert) { + lastNode = convert->copy_with_new_inputs({lastNode}); + } + if (subtract) { + std::shared_ptr input1 = nullptr; + if (subtractConvert) { + input1 = subtractConvert; + } else { + input1 = subtractConstant; + } + lastNode = subtract->copy_with_new_inputs({lastNode, input1}); + } + if (multiply) { + lastNode = multiply->copy_with_new_inputs({lastNode, multiplyConstant}); + } + return lastNode; +} + int FakeQuantizeDequantization::fillDequantizationParams( const std::shared_ptr& elementwise, std::shared_ptr& convert, diff --git a/src/common/low_precision_transformations/src/low_precision.cpp b/src/common/low_precision_transformations/src/low_precision.cpp index 715bd2ea0b5..5d21e3d4bd3 100644 --- a/src/common/low_precision_transformations/src/low_precision.cpp +++ b/src/common/low_precision_transformations/src/low_precision.cpp @@ -38,6 +38,7 @@ // general transformations #include "low_precision/add.hpp" +#include "low_precision/assign_and_read_value.hpp" #include "low_precision/avg_pool.hpp" #include "low_precision/clamp.hpp" #include "low_precision/convolution.hpp" @@ -207,6 +208,7 @@ bool ngraph::pass::low_precision::LowPrecision::run_on_model(const std::shared_p std::shared_ptr common = manager.register_pass(); common->add_matcher(params); + common->add_matcher(f, params); common->add_matcher(params); common->add_matcher(params); common->add_matcher(params); diff --git a/src/common/low_precision_transformations/src/network_helper.cpp b/src/common/low_precision_transformations/src/network_helper.cpp index 171f841f4cc..d7b5a9890d0 100644 --- a/src/common/low_precision_transformations/src/network_helper.cpp +++ b/src/common/low_precision_transformations/src/network_helper.cpp @@ -37,17 +37,6 @@ bool NetworkHelper::is_castable_to_one_of(NodeTypeInfo type, const std::unordere return false; } -bool NetworkHelper::notAllChildrensAreFQ(const NodeVector& childrens) { - // NOTE: This check was added for models that don't have FQ after AvgPool - // They will have transparent precision as it was in old LPT. - for (const auto& child : childrens) { - if (!ov::is_type(child)) { - return true; - } - } - return false; -} - // Collect and return a vector with all nodes that consumes any of the `node` output std::vector> NetworkHelper::consumer_inputs(std::shared_ptr node) { std::vector> result; @@ -199,15 +188,6 @@ size_t NetworkHelper::getGroupsCount(std::shared_ptr layer) { } } -// Assumin tensor in NC... layout, append necessary number of 1s to shape to align it to a give rank -Shape NetworkHelper::alignShapeForChannelDim(const Shape& shape, Rank rank) { - assert(shape.size() == 1); - assert(rank.is_static()); - Shape result = shape; - result.resize(rank.get_length() - 1, 1); - return result; -} - void NetworkHelper::removeLayer(std::shared_ptr layer) { ngraph::replace_output_update_name(layer->output(0), layer->input_value(0)); } @@ -1359,9 +1339,12 @@ FakeQuantizeDequantization NetworkHelper::getDequantization(const std::shared_pt const std::shared_ptr convert = ov::as_type_ptr(dataNode.get_node_shared_ptr()); if (convert != nullptr) { - if ((convert->input(0).get_element_type() != element::i8) && (convert->input(0).get_element_type() != element::u8) && - (convert->input(0).get_element_type() != element::i4) && (convert->input(0).get_element_type() != element::u4) && - (convert->output(0).get_element_type() != element::f32)) { + auto defaultPrecisions = LayerTransformation::getDefaultPrecisions(); + auto el_type = convert->input(0).get_element_type(); + auto foundIt = std::find(defaultPrecisions.begin(), defaultPrecisions.end(), el_type); + if (foundIt == defaultPrecisions.end() && + el_type != element::i4 && el_type != element::u4 && + el_type != element::f32 && el_type != element::f16) { return FakeQuantizeDequantization(dataNode, nullptr, subtract, subtractConvert, subtractConstant, multiply, multiplyConstant); } dataNode = convert->get_input_source_output(0); diff --git a/src/tests/functional/inference_engine/lp_transformations/assign_and_read_value_transformation.cpp b/src/tests/functional/inference_engine/lp_transformations/assign_and_read_value_transformation.cpp new file mode 100644 index 00000000000..0a9b125dea4 --- /dev/null +++ b/src/tests/functional/inference_engine/lp_transformations/assign_and_read_value_transformation.cpp @@ -0,0 +1,200 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "layer_transformation.hpp" + +#include +#include +#include + +#include +#include + +#include "common_test_utils/ngraph_test_utils.hpp" +#include "lpt_ngraph_functions/common/dequantization_operations.hpp" +#include "lpt_ngraph_functions/assign_and_read_value_function.hpp" +#include "simple_low_precision_transformer.hpp" +#include "low_precision/layer_transformation.hpp" + + +namespace { +using namespace testing; +using namespace ngraph::pass; +using namespace ngraph; + +class AssignTransformationTestValues { +public: + class Actual { + public: + std::vector constantValue; + ngraph::builder::subgraph::DequantizationOperations dequantization; + }; + + class Expected { + public: + std::vector constantValue; + ngraph::builder::subgraph::DequantizationOperations dequantizationBefore; + ngraph::builder::subgraph::DequantizationOperations dequantizationAfter; + }; + + TestTransformationParams params; + Actual actual; + Expected expected; + bool FQAfterReadValue; +}; + +typedef std::tuple < + ngraph::PartialShape, // input shape + element::Type, // input precision + element::Type, // precision before dequantization + size_t, // opset version + AssignTransformationTestValues // test values +> AssignTransformationParams; + +class AssignTransformation : public LayerTransformation, public testing::WithParamInterface { +public: + void SetUp() override { + const ngraph::PartialShape inputShape = std::get<0>(GetParam()); + const element::Type precision = std::get<1>(GetParam()); + const element::Type precisionBeforeDequantization = std::get<2>(GetParam()); + const size_t opsetVersion = std::get<3>(GetParam()); + const AssignTransformationTestValues testValues = std::get<4>(GetParam()); + low_precision::LayerTransformation::setDefaultPrecisions({ + ngraph::element::u8, ngraph::element::i8, + ngraph::element::u16, ngraph::element::i16, + ngraph::element::u32, ngraph::element::i32 + }); + actualFunction = ngraph::builder::subgraph::AssignAndReadValueFunction::getOriginal( + inputShape, + precision, + precisionBeforeDequantization, + opsetVersion, + testValues.FQAfterReadValue, + testValues.actual.constantValue, + testValues.actual.dequantization); + + SimpleLowPrecisionTransformer transformer; + transformer.add(actualFunction, testValues.params); + transformer.transform(actualFunction); + + referenceFunction = ngraph::builder::subgraph::AssignAndReadValueFunction::getReference( + inputShape, + precision, + precisionBeforeDequantization, + opsetVersion, + testValues.FQAfterReadValue, + testValues.expected.constantValue, + testValues.expected.dequantizationBefore, + testValues.expected.dequantizationAfter); + } + + static std::string getTestCaseName(testing::TestParamInfo obj) { + const ngraph::PartialShape inputShape = std::get<0>(obj.param); + const element::Type precision = std::get<1>(obj.param); + const element::Type precisionBeforeDequantization = std::get<2>(obj.param); + const size_t opsetVersion = std::get<3>(obj.param); + const AssignTransformationTestValues testValues = std::get<4>(obj.param); + + std::ostringstream result; + result << toString(testValues.params) << "_" << + inputShape << "_" << precision << "_" << + opsetVersion << "_" << testValues.FQAfterReadValue << "_" << + precisionBeforeDequantization << "_" << + testValues.actual.constantValue << "_" << + testValues.actual.dequantization; + return result.str(); + } +}; + +TEST_P(AssignTransformation, CompareFunctions) { + actualFunction->validate_nodes_and_infer_types(); + auto res = compare_functions(referenceFunction, actualFunction, true, true); + ASSERT_TRUE(res.first) << res.second; + + ASSERT_TRUE(LayerTransformation::allNamesAreUnique(actualFunction)) << "Not all names are unique"; +} + +namespace testValues1 { +const std::vector inputShapes = { + ngraph::PartialShape({ 1, 3, 224, 224 }), +}; + +const element::TypeVector precisions = { + element::f16, element::f32 +}; + +const element::TypeVector precisionsBeforeDequantizations = { + element::i8, element::u8, + element::i16, element::u16, + element::i32, element::u32, +}; + +const std::vector opsetVersions = { + 3, + 6 +}; + +const std::vector testValues = { + // general case, no subtract, FQ after ReadValue + { + LayerTransformation::createParamsU8I8(), + // ActualValues + { + {0}, + {{ngraph::element::f32}, {}, {3.f}} + }, + // ExpectedValues + { + {0}, + {{}, {}, {}}, + {{ngraph::element::f32}, {}, {3.f}} + }, + true + }, + // no FQ after ReadValue + { + LayerTransformation::createParamsU8I8(), + // ActualValues + { + {0}, + {{ngraph::element::f32}, {}, {3.f}} + }, + // ExpectedValues + { + {0}, + {{}, {}, {}}, + {{ngraph::element::f32}, {}, {3.f}} + }, + false + }, + // non-zero constant + { + LayerTransformation::createParamsU8I8(), + // ActualValues + { + {5}, + {{ngraph::element::f32}, {}, {3.f}} + }, + // ExpectedValues + { + {5}, + {{ngraph::element::f32}, {}, {3.f}}, + {} + }, + false + }, +}; + +INSTANTIATE_TEST_SUITE_P( + smoke_LPT, + AssignTransformation, + ::testing::Combine( + ::testing::ValuesIn(inputShapes), + ::testing::ValuesIn(precisions), + ::testing::ValuesIn(precisionsBeforeDequantizations), + ::testing::ValuesIn(opsetVersions), + ::testing::ValuesIn(testValues)), + AssignTransformation::getTestCaseName); +} // namespace testValues1 +} // namespace diff --git a/src/tests/functional/inference_engine/lp_transformations/simple_low_precision_transformer.hpp b/src/tests/functional/inference_engine/lp_transformations/simple_low_precision_transformer.hpp index c349056f6de..9f68b6d0542 100644 --- a/src/tests/functional/inference_engine/lp_transformations/simple_low_precision_transformer.hpp +++ b/src/tests/functional/inference_engine/lp_transformations/simple_low_precision_transformer.hpp @@ -24,6 +24,10 @@ public: void add(const TestTransformationParams& params) { commonGraphRewrite->add_matcher(TestTransformationParams::toParams(params)); } + template + void add(const std::shared_ptr function, const TestTransformationParams& params) { + commonGraphRewrite->add_matcher(function, TestTransformationParams::toParams(params)); + } void transform(std::shared_ptr& function); bool run_on_model(const std::shared_ptr& m) override; diff --git a/src/tests/functional/plugin/cpu/shared_tests_instances/low_precision_transformations/assign_and_read_value_transformation.cpp b/src/tests/functional/plugin/cpu/shared_tests_instances/low_precision_transformations/assign_and_read_value_transformation.cpp new file mode 100644 index 00000000000..0da67ab4379 --- /dev/null +++ b/src/tests/functional/plugin/cpu/shared_tests_instances/low_precision_transformations/assign_and_read_value_transformation.cpp @@ -0,0 +1,55 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include + +#include "low_precision_transformations/assign_and_read_value_transformation.hpp" + + +using namespace LayerTestsDefinitions; + +namespace { +const std::vector netPrecisions = { + ngraph::element::f32, + // ngraph::element::f16 +}; + +const std::vector opsetVersions = { + // 3, // no evaluate for opset 3 in ngraph + 6 +}; + +const std::vector trasformationParamValues = { + LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParams(), + LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParams().setUpdatePrecisions(false), +}; + +const std::vector params{ + // u8 + { + { 256ul, ngraph::Shape{ 1, 1, 1, 1 }, { 0.f }, { 25.5f }, { 0.f }, { 12.8f } }, + }, + // u16 + { + { 65536ul, ngraph::Shape{ 1, 1, 1, 1 }, { 0.f }, { 25.5f }, { 0.f }, { 12.8f } }, + }, + // u32 + { + { 4294967296ul, ngraph::Shape{ 1, 1, 1, 1 }, { 0.f }, { 25.5f }, { 0.f }, { 12.8f } }, + }, +}; + +INSTANTIATE_TEST_SUITE_P(smoke_LPT, AssignAndReadValueTransformation, + ::testing::Combine( + ::testing::ValuesIn(netPrecisions), + ::testing::Values(ngraph::PartialShape({ 1, 3, 16, 16 })), + ::testing::ValuesIn(opsetVersions), + ::testing::Values(CommonTestUtils::DEVICE_CPU), + ::testing::ValuesIn(trasformationParamValues), + ::testing::ValuesIn(params)), + AssignAndReadValueTransformation::getTestCaseName); + +} // namespace diff --git a/src/tests/functional/plugin/gpu/shared_tests_instances/low_precision_transformations/assign_and_read_value_transformation.cpp b/src/tests/functional/plugin/gpu/shared_tests_instances/low_precision_transformations/assign_and_read_value_transformation.cpp new file mode 100644 index 00000000000..390514c1884 --- /dev/null +++ b/src/tests/functional/plugin/gpu/shared_tests_instances/low_precision_transformations/assign_and_read_value_transformation.cpp @@ -0,0 +1,55 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include + +#include "low_precision_transformations/assign_and_read_value_transformation.hpp" + + +using namespace LayerTestsDefinitions; + +namespace { +const std::vector netPrecisions = { + ngraph::element::f32, + // ngraph::element::f16 +}; + +const std::vector opsetVersions = { + // 3, // no evaluate for opset 3 in ngraph + // 6 // not supported on GPU +}; + +const std::vector trasformationParamValues = { + LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParams(), + LayerTestsUtils::LayerTransformationParamsNGraphFactory::createParams().setUpdatePrecisions(false), +}; + +const std::vector params{ + // u8 + { + { 256ul, ngraph::Shape{ 1, 1, 1, 1 }, { 0.f }, { 25.5f }, { 0.f }, { 12.8f } }, + }, + // u16 + { + { 65536ul, ngraph::Shape{ 1, 1, 1, 1 }, { 0.f }, { 25.5f }, { 0.f }, { 12.8f } }, + }, + // u32 + { + { 4294967296ul, ngraph::Shape{ 1, 1, 1, 1 }, { 0.f }, { 25.5f }, { 0.f }, { 12.8f } }, + }, +}; + +INSTANTIATE_TEST_SUITE_P(smoke_LPT, AssignAndReadValueTransformation, + ::testing::Combine( + ::testing::ValuesIn(netPrecisions), + ::testing::Values(ngraph::PartialShape({ 1, 3, 16, 16 })), + ::testing::ValuesIn(opsetVersions), + ::testing::Values(CommonTestUtils::DEVICE_GPU), + ::testing::ValuesIn(trasformationParamValues), + ::testing::ValuesIn(params)), + AssignAndReadValueTransformation::getTestCaseName); + +} // namespace diff --git a/src/tests/functional/plugin/shared/include/low_precision_transformations/assign_and_read_value_transformation.hpp b/src/tests/functional/plugin/shared/include/low_precision_transformations/assign_and_read_value_transformation.hpp new file mode 100644 index 00000000000..3a5adf331e1 --- /dev/null +++ b/src/tests/functional/plugin/shared/include/low_precision_transformations/assign_and_read_value_transformation.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "shared_test_classes/base/low_precision_transformations/layer_transformation.hpp" +#include "lpt_ngraph_functions/common/fake_quantize_on_data.hpp" +#include "lpt_ngraph_functions/common/dequantization_operations.hpp" + +namespace LayerTestsDefinitions { +class AssignAndReadValueTransformationParam { +public: + ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantize; +}; + +typedef std::tuple < + ngraph::element::Type, // input precision + ngraph::PartialShape, // input shape + size_t, // opset version + std::string, // device + ngraph::pass::low_precision::LayerTransformation::Params, // transformation params + AssignAndReadValueTransformationParam // test params +> AssignAndReadValueTransformationParams; + +class AssignAndReadValueTransformation : + 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/assign_and_read_value_transformation.cpp b/src/tests/functional/plugin/shared/src/low_precision_transformations/assign_and_read_value_transformation.cpp new file mode 100644 index 00000000000..43123a36cd1 --- /dev/null +++ b/src/tests/functional/plugin/shared/src/low_precision_transformations/assign_and_read_value_transformation.cpp @@ -0,0 +1,49 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "low_precision_transformations/assign_and_read_value_transformation.hpp" +#include +#include +#include +#include + +#include "lpt_ngraph_functions/assign_and_read_value_function.hpp" + +namespace LayerTestsDefinitions { + +std::string AssignAndReadValueTransformation::getTestCaseName(const testing::TestParamInfo& obj) { + ngraph::element::Type netPrecision; + ngraph::PartialShape inputShape; + size_t opset; + std::string targetDevice; + ngraph::pass::low_precision::LayerTransformation::Params params; + AssignAndReadValueTransformationParam param;; + std::tie(netPrecision, inputShape, opset, targetDevice, params, param) = obj.param; + + std::ostringstream result; + result << getTestCaseNameByParams(netPrecision, inputShape, targetDevice, params) << "_" << + param.fakeQuantize << "_" << opset; + return result.str(); +} + +void AssignAndReadValueTransformation::SetUp() { + ngraph::element::Type netPrecision; + ngraph::PartialShape inputShape; + size_t opset; + ngraph::pass::low_precision::LayerTransformation::Params params; + AssignAndReadValueTransformationParam param; + std::tie(netPrecision, inputShape, opset, targetDevice, params, param) = this->GetParam(); + + function = ngraph::builder::subgraph::AssignAndReadValueFunction::getOriginal( + netPrecision, + inputShape, + param.fakeQuantize, + opset); +} + +TEST_P(AssignAndReadValueTransformation, CompareWithRefImpl) { + Run(); +}; + +} // namespace LayerTestsDefinitions diff --git a/src/tests/ngraph_helpers/lpt_ngraph_functions/include/lpt_ngraph_functions/assign_and_read_value_function.hpp b/src/tests/ngraph_helpers/lpt_ngraph_functions/include/lpt_ngraph_functions/assign_and_read_value_function.hpp new file mode 100644 index 00000000000..d5113da7b1b --- /dev/null +++ b/src/tests/ngraph_helpers/lpt_ngraph_functions/include/lpt_ngraph_functions/assign_and_read_value_function.hpp @@ -0,0 +1,46 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include +#include "lpt_ngraph_functions/common/fake_quantize_on_data.hpp" +#include "lpt_ngraph_functions/common/dequantization_operations.hpp" + +namespace ngraph { +namespace builder { +namespace subgraph { + +class AssignAndReadValueFunction { +public: + static std::shared_ptr getOriginal( + const ngraph::PartialShape& inputShape, + const element::Type& inputPrecision, + const ngraph::element::Type precisionBeforeDequantization, + const size_t opsetVersion, + const bool FQAfterReadValue, + const std::vector& constantValue, + const ngraph::builder::subgraph::DequantizationOperations& dequantization); + + static std::shared_ptr getOriginal( + const ngraph::element::Type originalFunctionPrecision, + const ngraph::PartialShape& inputShape, + const ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantize, + const size_t opsetVersion); + + static std::shared_ptr getReference( + const ngraph::PartialShape& inputShape, + const element::Type& inputPrecision, + const ngraph::element::Type precisionBeforeDequantization, + const size_t opsetVersion, + const bool FQAfterReadValue, + const std::vector& constantValue, + const ngraph::builder::subgraph::DequantizationOperations& dequantizationBefore, + const ngraph::builder::subgraph::DequantizationOperations& dequantizationAfter); +}; +} // namespace subgraph +} // namespace builder +} // namespace ngraph diff --git a/src/tests/ngraph_helpers/lpt_ngraph_functions/src/assign_and_read_value_function.cpp b/src/tests/ngraph_helpers/lpt_ngraph_functions/src/assign_and_read_value_function.cpp new file mode 100644 index 00000000000..67e50de1852 --- /dev/null +++ b/src/tests/ngraph_helpers/lpt_ngraph_functions/src/assign_and_read_value_function.cpp @@ -0,0 +1,185 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include + + +#include +#include "ngraph_functions/subgraph_builders.hpp" +#include "openvino/op/util/variable.hpp" +#include + +#include "lpt_ngraph_functions/common/builders.hpp" +#include "lpt_ngraph_functions/assign_and_read_value_function.hpp" +#include "low_precision/network_helper.hpp" + +namespace ngraph { +namespace builder { +namespace subgraph { + +std::shared_ptr AssignAndReadValueFunction::getOriginal( + const ngraph::PartialShape& inputShape, + const element::Type& inputPrecision, + const ngraph::element::Type precisionBeforeDequantization, + const size_t opsetVersion, + const bool FQAfterReadValue, + const std::vector& constantValue, + const ngraph::builder::subgraph::DequantizationOperations& dequantization) { + const auto input = std::make_shared(inputPrecision, inputShape); + const auto defaultConstant = std::make_shared(inputPrecision, inputShape.get_shape(), constantValue); + const auto variable = std::make_shared(VariableInfo{inputShape.get_shape(), inputPrecision, "id"}); + std::shared_ptr readValue; + if (opsetVersion == 6) { + readValue = std::make_shared(defaultConstant, variable); + } else if (opsetVersion == 3) { + readValue = std::make_shared(defaultConstant, "id"); + } else { + throw std::runtime_error("Unknown opset version"); + } + std::shared_ptr lastNode = readValue; + if (FQAfterReadValue) { + lastNode = builder::subgraph::makeFakeQuantize( + lastNode, + element::f32, + FakeQuantizeOnData{256ul, Shape{}, {0}, {2.55f}, {0}, {2.55f}}); + } + const auto add = std::make_shared(lastNode, input); + const auto FQAfterAdd = builder::subgraph::makeFakeQuantizeTypeRelaxed( + add, + element::f32, + FakeQuantizeOnData{256ul, Shape{}, {0}, {2.55f}, {0}, {2.55f}, precisionBeforeDequantization}); + auto deqStructure = dequantization; + deqStructure.multiply.outPrecision = inputPrecision; + const auto dequantizationOp = makeDequantization(FQAfterAdd, deqStructure); + std::shared_ptr assign; + if (opsetVersion == 6) { + assign = std::make_shared(dequantizationOp, variable); + } else { + assign = std::make_shared(dequantizationOp, "id"); + } + assign->add_control_dependency(readValue); + add->set_friendly_name("output"); + + ngraph::ResultVector results{ std::make_shared(add) }; + ngraph::SinkVector sinks{ as_type_ptr(assign) }; + return std::make_shared(results, sinks, ngraph::ParameterVector{ input }, "AssignAndReadValueFunction"); +} + +std::shared_ptr AssignAndReadValueFunction::getOriginal( + const ngraph::element::Type precision, + const ngraph::PartialShape& inputShape, + const ngraph::builder::subgraph::FakeQuantizeOnData fakeQuantize, + const size_t opsetVersion) { + const auto input = std::make_shared(precision, inputShape); + const auto defaultConstant = std::make_shared(precision, inputShape.get_shape(), std::vector{0}); + const auto variable = std::make_shared(VariableInfo{inputShape.get_shape(), precision, "id"}); + std::shared_ptr readValue; + if (opsetVersion == 6) { + readValue = std::make_shared(defaultConstant, variable); + } else if (opsetVersion == 3) { + readValue = std::make_shared(defaultConstant, "id"); + } else { + throw std::runtime_error("Unknown opset version"); + } + std::shared_ptr lastNode = readValue; + lastNode = builder::subgraph::makeFakeQuantize( + lastNode, + element::f32, + FakeQuantizeOnData{256ul, Shape{}, {0}, {2.55f}, {0}, {2.55f}}); + const auto add = std::make_shared(lastNode, input); + const auto FQAfterAdd = fakeQuantize.empty() ? nullptr : + ngraph::builder::makeFakeQuantize( + add, + precision, + fakeQuantize.quantizationLevel, + fakeQuantize.constantShape, + fakeQuantize.inputLowValues, + fakeQuantize.inputHighValues, + fakeQuantize.outputLowValues, + fakeQuantize.outputHighValues); + std::shared_ptr assign; + if (opsetVersion == 6) { + assign = std::make_shared(FQAfterAdd, variable); + } else { + assign = std::make_shared(FQAfterAdd, "id"); + } + assign->add_control_dependency(readValue); + add->set_friendly_name("output"); + + ngraph::ResultVector results{ std::make_shared(add) }; + ngraph::SinkVector sinks{ as_type_ptr(assign) }; + return std::make_shared(results, sinks, ngraph::ParameterVector{ input }, "AssignAndReadValueFunction"); +} + +std::shared_ptr AssignAndReadValueFunction::getReference( + const ngraph::PartialShape& inputShape, + const element::Type& inputPrecision, + const ngraph::element::Type precisionBeforeDequantization, + const size_t opsetVersion, + const bool FQAfterReadValue, + const std::vector& constantValue, + const ngraph::builder::subgraph::DequantizationOperations& dequantizationBefore, + const ngraph::builder::subgraph::DequantizationOperations& dequantizationAfter) { + const auto input = std::make_shared(inputPrecision, inputShape); + auto constantPrecision = precisionBeforeDequantization; + if (constantValue != std::vector{0}) { + constantPrecision = inputPrecision; + } + const auto defaultConstant = std::make_shared(constantPrecision, inputShape.get_shape(), constantValue); + const auto variable = std::make_shared(VariableInfo{inputShape.get_shape(), constantPrecision, "id"}); + std::shared_ptr readValue; + if (opsetVersion == 6) { + readValue = std::make_shared(defaultConstant, variable); + } else if (opsetVersion == 3) { + readValue = std::make_shared(defaultConstant, "id"); + } else { + throw std::runtime_error("Unknown opset version"); + } + std::shared_ptr lastNode = readValue; + + auto deqStructureAfter = dequantizationAfter; + if (FQAfterReadValue) { + DequantizationOperations tempDequantization; + tempDequantization.convert = dequantizationAfter.convert; + tempDequantization.subtract = dequantizationAfter.subtract; + lastNode = makeDequantization(lastNode, tempDequantization); + } else { + deqStructureAfter.multiply.outPrecision = inputPrecision; + lastNode = makeDequantization(lastNode, deqStructureAfter); + } + + if (FQAfterReadValue) { + lastNode = builder::subgraph::makeFakeQuantizeTypeRelaxed( + lastNode, + element::f32, + FakeQuantizeOnData{256ul, Shape{}, {0}, {2.55f / dequantizationAfter.multiply.values[0]}, {0}, {2.55f}, inputPrecision}); + } + const auto add = std::make_shared(lastNode, input); + const auto FQAfterAdd = builder::subgraph::makeFakeQuantizeTypeRelaxed( + add, + element::f32, + FakeQuantizeOnData{256ul, Shape{}, {0}, {2.55f}, {0}, {2.55f}, precisionBeforeDequantization}); + + auto deqStructureBefore = dequantizationBefore; + deqStructureBefore.multiply.outPrecision = inputPrecision; + const auto dequantizationBeforeStructure = makeDequantization(FQAfterAdd, deqStructureBefore); + std::shared_ptr assign; + if (opsetVersion == 6) { + assign = std::make_shared(dequantizationBeforeStructure, variable); + } else { + assign = std::make_shared(dequantizationBeforeStructure, "id"); + } + assign->add_control_dependency(readValue); + add->set_friendly_name("output"); + + ngraph::ResultVector results{ std::make_shared(add) }; + ngraph::SinkVector sinks{ as_type_ptr(assign) }; + return std::make_shared(results, sinks, ngraph::ParameterVector{ input }, "AssignAndReadValueFunction"); +} + +} // namespace subgraph +} // namespace builder +} // namespace ngraph