Files
openvino/src/plugins/intel_gna/src/ops/util/util.hpp
Nadezhda Ageeva a4655bb6b3 [GNA] Insert identity using ngraph transformation (#13317)
* [GNA] Insert identity using ngraph transformation

* Update src/plugins/intel_gna/src/ops/identity.hpp

Co-authored-by: Szymon Irzabek <szymon.jakub.irzabek@intel.com>

* Update src/plugins/intel_gna/src/transformations/rt_info/gna_precision_change_flag.hpp

Co-authored-by: Szymon Irzabek <szymon.jakub.irzabek@intel.com>

* Update src/plugins/intel_gna/src/transformations/rt_info/gna_precision_change_flag.cpp

Co-authored-by: Szymon Irzabek <szymon.jakub.irzabek@intel.com>

* Update src/plugins/intel_gna/src/transformations/insert_identity_layer.hpp

Co-authored-by: Szymon Irzabek <szymon.jakub.irzabek@intel.com>

* Rewrites pass with Identity insertion using recusrive function. Adds test for Split. Adds comments

* Change namespace for element type

Co-authored-by: Szymon Irzabek <szymon.jakub.irzabek@intel.com>
2022-10-19 20:27:55 +03:00

280 lines
12 KiB
C++

// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <vector>
#include <memory>
#include <transformations/utils/utils.hpp>
#include <ngraph/opsets/opset9.hpp>
#include <ngraph/opsets/opset8.hpp>
#include <ngraph/opsets/opset7.hpp>
#include <legacy/ngraph_ops/crop_ie.hpp>
#include <legacy/ngraph_ops/convolution_ie.hpp>
#include <legacy/ngraph_ops/eltwise.hpp>
#include <legacy/ngraph_ops/fully_connected.hpp>
#include <legacy/ngraph_ops/scaleshift.hpp>
#include <legacy/ngraph_ops/power.hpp>
#include <legacy/ngraph_ops/relu_ie.hpp>
#include "backend/gna_limitations.hpp"
#include "layers/gna_permute.hpp"
#include <transformations/utils/utils.hpp>
#include <transformations/rt_info/gna_transpose_fusable.hpp>
#include "ops/copy.hpp"
#include "ops/identity.hpp"
#include "ops/pwl.hpp"
namespace ov {
namespace intel_gna {
namespace ngraph_util {
template <typename T>
static bool get_constant_value(const std::shared_ptr<ngraph::opset8::Constant>& constant, std::vector<double>& values) {
using A = typename ov::element_type_traits<T::value>::value_type;
const auto& v = constant->get_vector<A>();
std::copy(v.begin(), v.end(), std::back_inserter(values));
return true;
}
static bool get_constant_value(std::tuple<>&&, const std::shared_ptr<ngraph::opset8::Constant>&, std::vector<double>&) {
return false;
}
template<typename T, typename ...Types>
static bool get_constant_value(std::tuple<T, Types...>&&,
const std::shared_ptr<ngraph::opset8::Constant>& constant, std::vector<double>& values) {
return constant->get_element_type() == T::value &&
get_constant_value<T>(constant, values) ||
get_constant_value(std::tuple<Types...>(), constant, values);
}
static bool get_constant_value(const std::shared_ptr<ngraph::opset8::Constant>& constant, std::vector<double>& values) {
return get_constant_value(std::tuple<std::integral_constant<ov::element::Type_t, ov::element::i32>,
std::integral_constant<ov::element::Type_t, ov::element::i64>,
std::integral_constant<ov::element::Type_t, ov::element::u32>,
std::integral_constant<ov::element::Type_t, ov::element::u64>,
std::integral_constant<ov::element::Type_t, ov::element::f16>,
std::integral_constant<ov::element::Type_t, ov::element::f32>,
std::integral_constant<ov::element::Type_t, ov::element::f64>>(),
constant,
values);
}
static bool get_constant_value(const std::shared_ptr<ngraph::opset8::Constant>& constant, double& value) {
std::vector<double> values;
if (!get_constant_value(constant, values)) {
return false;
}
if (values.empty() || values.size() > 1) {
throw std::runtime_error("The size of values is more than 1.");
}
value = values[0];
return true;
}
static bool is_aligned_split(const std::shared_ptr<ngraph::Node> input_op, size_t input_op_out_index) {
size_t offset = 0;
if (std::dynamic_pointer_cast<ngraph::opset8::Split>(input_op) || std::dynamic_pointer_cast<ngraph::opset8::VariadicSplit>(input_op)) {
for (size_t index = 0; index < input_op_out_index; index++) {
size_t outputSize = ngraph::shape_size(input_op->get_output_shape(index));
offset += outputSize * GNAPluginNS::GNALimitations::bytesPerSplitElement;
}
}
return (offset == ALIGN64(offset));
}
static bool is_crop_affined(std::shared_ptr<ngraph::Node> node) {
auto crop = std::dynamic_pointer_cast<ngraph::op::CropIE>(node);
if (crop != nullptr && !crop->offset.empty()) {
return GNAPluginNS::GNALimitations::isCropAffinedOffset(crop->offset.back());
}
return false;
}
// this not only mathematically trivial
static bool is_trivial_transpose(std::shared_ptr<ngraph::Node> node) {
auto transpose = std::dynamic_pointer_cast<ngraph::opset8::Transpose>(node);
if (!transpose) return false;
if (transpose->get_input_size() == 0)
return false; // unsupported case
if (ov::intel_gna::rt_info::is_transpose_fusable(transpose))
return true;
auto transpose_const = std::dynamic_pointer_cast<ngraph::op::Constant>(transpose->input_value(1).get_node_shared_ptr());
if (!transpose_const) return false;
auto node_order = transpose_const->cast_vector<int64_t>();
auto input = transpose->input(0).get_source_output().get_node_shared_ptr();
auto input_order = transpose->get_input_shape(0);
return GNAPluginNS::isTrivialPermute(node_order, input_order);
}
inline std::shared_ptr<ov::Node> get_prev_node_skipping_certain(const std::shared_ptr<ngraph::Node>& node,
const std::function<bool(std::shared_ptr<ngraph::Node>)>& skip) {
auto current_node = node;
while (skip(current_node)) {
current_node = current_node->get_input_node_shared_ptr(0);
}
return current_node;
}
inline std::shared_ptr<ov::Node> get_next_node_skipping_certain(const std::shared_ptr<ngraph::Node>& node,
const std::function<bool(std::shared_ptr<ngraph::Node>)>& skip) {
auto current_node = node;
while (skip(current_node)) {
current_node = current_node->output(0).get_target_inputs().begin()->get_node()->shared_from_this();
}
return current_node;
}
inline bool is_gna_non_functional_node(const std::shared_ptr<ngraph::Node>& node) {
return std::dynamic_pointer_cast<ngraph::opset8::Reshape>(node) ||
std::dynamic_pointer_cast<ngraph::opset8::Squeeze>(node) ||
std::dynamic_pointer_cast<ngraph::opset8::Unsqueeze>(node) ||
is_trivial_transpose(node);
}
inline bool is_one_dim_shape(const ov::Shape& dims) {
return std::count_if(std::begin(dims), std::end(dims), [](size_t dim) { return dim != 1; }) <= 1;
}
inline bool is_one_dim_shapes(const ov::Shape& in_dims, const ov::Shape& out_dims) {
return is_one_dim_shape(in_dims) && is_one_dim_shape(out_dims);
}
static bool is_power_activation(const ov::Node* node) noexcept {
if (auto power_op = dynamic_cast<const ngraph::opset9::Power*>(node)) {
auto const_node = std::dynamic_pointer_cast<ngraph::opset9::Constant>(power_op->get_input_node_shared_ptr(1));
if (!const_node)
return false;
float value;
if (!ngraph::op::util::get_single_value(const_node, value)) {
return true;
}
return (1.0f != value);
} else if (auto power_op = std::dynamic_pointer_cast<ngraph::op::PowerIE>(node)) {
return (1.0f != power_op->power);
}
return false;
}
static bool is_power_activation(const std::shared_ptr<ngraph::Node>& node) noexcept {
return is_power_activation(node.get());
}
static bool is_eltwise_mul(const ngraph::Output<ngraph::Node>& node) {
auto eltwise = std::dynamic_pointer_cast<ngraph::op::Eltwise>(node.get_node_shared_ptr());
if (!eltwise) return false;
return eltwise->eltwise_type == ELTWISE_TYPE::Prod;
}
static bool is_eltwise_add(const ngraph::Output<ngraph::Node>& node) {
auto eltwise = std::dynamic_pointer_cast<ngraph::op::Eltwise>(node.get_node_shared_ptr());
if (!eltwise) return false;
return eltwise->eltwise_type == ELTWISE_TYPE::Sum;
}
static bool is_pooling(const ngraph::Output<ngraph::Node>& node) {
return (std::dynamic_pointer_cast<ngraph::opset7::MaxPool>(node.get_node_shared_ptr()) != nullptr);
}
template <typename T>
static bool is_Tbit_fq(const std::shared_ptr<ngraph::Node>& node) {
auto fq_node = std::dynamic_pointer_cast<ngraph::opset9::FakeQuantize>(node);
if (!fq_node)
return false;
auto levels = fq_node->get_levels();
return std::numeric_limits<T>::max() == levels;
}
static bool is_32bit_fq(const std::shared_ptr<ngraph::Node>& node) {
return is_Tbit_fq<uint32_t>(node);
}
static bool is_16bit_fq(const std::shared_ptr<ngraph::Node>& node) {
return is_Tbit_fq<uint16_t>(node);
}
static bool is_8bit_fq(const std::shared_ptr<ngraph::Node>& node) {
return is_Tbit_fq<uint8_t>(node);
}
static bool is_activation(const ov::Node* node) noexcept {
return ((dynamic_cast<const ngraph::opset9::Clamp*>(node) != nullptr) ||
(dynamic_cast<const ngraph::opset9::Sigmoid*>(node) != nullptr) ||
(dynamic_cast<const ngraph::opset9::Relu*>(node) != nullptr) ||
(dynamic_cast<const ngraph::op::ReLUIE*>(node) != nullptr) ||
(dynamic_cast<const ngraph::opset9::Tanh*>(node) != nullptr) ||
(dynamic_cast<const ngraph::opset9::PRelu*>(node) != nullptr) ||
(dynamic_cast<const ngraph::opset9::Exp*>(node) != nullptr) ||
(dynamic_cast<const ngraph::opset9::Log*>(node) != nullptr) ||
(dynamic_cast<const ngraph::opset9::Sign*>(node) != nullptr) ||
(dynamic_cast<const ngraph::opset9::Abs*>(node) != nullptr) ||
(dynamic_cast<const ngraph::opset9::SoftSign*>(node) != nullptr) ||
is_power_activation(node) ||
(dynamic_cast<const ngraph::opset9::FakeQuantize*>(node) != nullptr) ||
(dynamic_cast<const ov::intel_gna::op::Pwl*>(node) != nullptr) ||
(dynamic_cast<const ov::intel_gna::op::Identity*>(node) != nullptr));
}
static bool is_activation(const std::shared_ptr<ngraph::Node>& node) noexcept {
return is_activation(node.get());
}
static bool is_gna_precision_agnostic(std::shared_ptr<ngraph::Node> node) {
return ((std::dynamic_pointer_cast<ngraph::opset9::VariadicSplit>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Split>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Slice>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Concat>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Reshape>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Squeeze>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Unsqueeze>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Transpose>(node) != nullptr) ||
(std::dynamic_pointer_cast<ov::intel_gna::op::Copy>(node) != nullptr) ||
((std::dynamic_pointer_cast<ngraph::op::CropIE>(node) != nullptr) && !is_crop_affined(node)));
}
static bool has_8bit_or_16_bit_output(const std::shared_ptr<ngraph::Node>& node) noexcept {
return ((ngraph::op::is_parameter(node)) ||
(ngraph::op::is_constant(node)) ||
(std::dynamic_pointer_cast<ngraph::opset9::ReadValue>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Assign>(node) != nullptr) ||
(is_activation(node) && (!is_32bit_fq(node))) ||
(is_8bit_fq(node) || (is_16bit_fq(node))) ||
is_gna_precision_agnostic(node));
}
static bool has_32bit_output(const std::shared_ptr<ngraph::Node>& node) {
return ((std::dynamic_pointer_cast<ngraph::op::FullyConnected>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::MatMul>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Convolution>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::op::ConvolutionIE>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Add>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::opset9::Multiply>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::op::Eltwise>(node) != nullptr) ||
(std::dynamic_pointer_cast<ngraph::op::ScaleShiftIE>(node) != nullptr) ||
is_pooling(node) ||
((std::dynamic_pointer_cast<ngraph::opset9::Power>(node) != nullptr) && !is_power_activation(node)) ||
((std::dynamic_pointer_cast<ngraph::op::PowerIE>(node) != nullptr) && !is_power_activation(node)) ||
is_crop_affined(node) ||
is_32bit_fq(node));
}
inline bool has_32bit_input(const std::shared_ptr<ngraph::Node>& node) {
return is_activation(node) || is_pooling(node);
}
} // namespace ngraph_util
} // namespace intel_gna
} // namespace ov