1083 lines
30 KiB
C++
1083 lines
30 KiB
C++
//*****************************************************************************
|
|
// Copyright 2017-2021 Intel Corporation
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//*****************************************************************************
|
|
|
|
#include <memory>
|
|
#include <ngraph/validation_util.hpp>
|
|
#include <sstream>
|
|
#include <typeindex>
|
|
#include <typeinfo>
|
|
|
|
#include "itt.hpp"
|
|
#include "ngraph/descriptor/input.hpp"
|
|
#include "ngraph/graph_util.hpp"
|
|
#include "ngraph/node.hpp"
|
|
#include "ngraph/op/constant.hpp"
|
|
#include "ngraph/op/parameter.hpp"
|
|
#include "ngraph/op/result.hpp"
|
|
#include "ngraph/pattern/matcher.hpp"
|
|
|
|
using namespace std;
|
|
using namespace ngraph;
|
|
|
|
atomic<size_t> Node::m_next_instance_id(0);
|
|
|
|
Node::Node(const Node& node)
|
|
: m_control_dependents(node.m_control_dependents)
|
|
, m_control_dependencies(node.m_control_dependencies)
|
|
// skip m_node_type -- will be generated automatically
|
|
, m_instance_id(m_next_instance_id.fetch_add(1))
|
|
, m_friendly_name(node.m_friendly_name)
|
|
// skip m_unique_name -- will be generated automatically
|
|
, m_provenance_tags(node.m_provenance_tags)
|
|
, m_provenance_group(node.m_provenance_group)
|
|
, m_inputs(node.m_inputs) // will be modified in the body
|
|
// skip m_outputs -- should be initialized outside
|
|
, m_op_annotations(node.m_op_annotations)
|
|
, m_rt_info(node.m_rt_info)
|
|
{
|
|
// cannot do it without copying node.m_inputs first due to too limiting const qualifiers
|
|
for (auto& input : m_inputs)
|
|
{
|
|
input = descriptor::Input(this, input.get_index(), input.get_output());
|
|
input.get_output().add_input(&input);
|
|
}
|
|
}
|
|
|
|
Node& Node::operator=(const Node& node)
|
|
{
|
|
this->m_control_dependents = node.m_control_dependents;
|
|
this->m_control_dependencies = node.m_control_dependencies;
|
|
this->m_instance_id = m_next_instance_id.fetch_add(1);
|
|
this->m_friendly_name = node.m_friendly_name;
|
|
this->m_provenance_tags = node.m_provenance_tags;
|
|
this->m_provenance_group = node.m_provenance_group;
|
|
this->m_inputs = node.m_inputs;
|
|
this->m_op_annotations = node.m_op_annotations;
|
|
this->m_rt_info = node.m_rt_info;
|
|
// cannot do it without copying node.m_inputs first due to too limiting const qualifiers
|
|
for (auto& input : m_inputs)
|
|
{
|
|
input = descriptor::Input(this, input.get_index(), input.get_output());
|
|
input.get_output().add_input(&input);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Node::Node(size_t output_size)
|
|
: Node()
|
|
{
|
|
set_output_size(output_size);
|
|
}
|
|
|
|
Node::Node(const OutputVector& arguments, size_t output_size)
|
|
: Node()
|
|
{
|
|
set_arguments(arguments);
|
|
set_output_size(output_size);
|
|
}
|
|
|
|
Node::~Node()
|
|
{
|
|
for (descriptor::Input& input : m_inputs)
|
|
{
|
|
if (input.has_output())
|
|
{
|
|
// This test adds 1 to the actual count, so a count of 2 means this input is the only
|
|
// reference to the node.
|
|
if (input.get_output().get_node().use_count() == 2)
|
|
{
|
|
// Don't want to trigger a deep recursive delete
|
|
NodeVector nodes{input.get_output().get_node()};
|
|
input.remove_output();
|
|
safe_delete(nodes, true);
|
|
return;
|
|
}
|
|
input.remove_output();
|
|
}
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Node> Node::copy_with_new_inputs(const OutputVector& inputs) const
|
|
{
|
|
return copy_with_new_inputs(inputs, get_control_dependencies());
|
|
}
|
|
|
|
Output<const Node> Node::get_default_output() const
|
|
{
|
|
return output(get_default_output_index());
|
|
}
|
|
|
|
Output<Node> Node::get_default_output()
|
|
{
|
|
return output(get_default_output_index());
|
|
}
|
|
|
|
size_t Node::get_default_output_index() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
size_t Node::no_default_index() const
|
|
{
|
|
NODE_VALIDATION_CHECK(this, false, "Default output not supported");
|
|
}
|
|
|
|
std::shared_ptr<Node>
|
|
Node::copy_with_new_inputs(const OutputVector& inputs,
|
|
const std::vector<std::shared_ptr<Node>>& control_dependencies) const
|
|
{
|
|
shared_ptr<Node> clone = clone_with_new_inputs(inputs);
|
|
for (auto& cdep : control_dependencies)
|
|
{
|
|
clone->add_control_dependency(cdep);
|
|
}
|
|
for (size_t i = 0; i < get_output_size(); i++)
|
|
{
|
|
clone->get_output_tensor(i).set_names(get_output_tensor(i).get_names());
|
|
}
|
|
return clone;
|
|
}
|
|
|
|
void Node::safe_delete(NodeVector& nodes, bool recurse)
|
|
{
|
|
for (auto& input : m_inputs)
|
|
{
|
|
if (input.has_output())
|
|
{
|
|
// This test adds 1 to the actual count, so a count of 2 means this input is the only
|
|
// reference to the node.
|
|
auto node = input.get_output().get_node();
|
|
if (node.use_count() == 2)
|
|
{
|
|
// Move the node from the input to nodes so we don't trigger a deep recursive delete
|
|
nodes.push_back(node);
|
|
}
|
|
input.remove_output();
|
|
}
|
|
}
|
|
if (recurse)
|
|
{
|
|
while (nodes.size() > 0)
|
|
{
|
|
auto node = nodes.back();
|
|
nodes.pop_back();
|
|
node->safe_delete(nodes, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Node::set_arguments(const NodeVector& arguments)
|
|
{
|
|
OutputVector outputs;
|
|
for (auto arg : arguments)
|
|
{
|
|
for (auto& output : arg->outputs())
|
|
{
|
|
outputs.push_back(output);
|
|
}
|
|
}
|
|
set_arguments(outputs);
|
|
}
|
|
|
|
void Node::set_arguments(const OutputVector& arguments)
|
|
{
|
|
// Add this node as a user of each argument.
|
|
size_t i = 0;
|
|
for (auto& output : arguments)
|
|
{
|
|
auto output_node = output.get_node();
|
|
auto& output_descriptor = output_node->m_outputs.at(output.get_index());
|
|
m_inputs.emplace_back(this, i++, output_descriptor);
|
|
}
|
|
}
|
|
|
|
descriptor::Input& Node::get_input_descriptor(size_t position)
|
|
{
|
|
while (m_inputs.size() <= position)
|
|
{
|
|
m_inputs.emplace_back(this, m_inputs.size());
|
|
}
|
|
return m_inputs.at(position);
|
|
}
|
|
|
|
descriptor::Output& Node::get_output_descriptor(size_t position)
|
|
{
|
|
while (m_outputs.size() <= position)
|
|
{
|
|
size_t i = m_outputs.size();
|
|
auto tensor_descriptor =
|
|
make_shared<descriptor::Tensor>(element::dynamic, PartialShape::dynamic(), this, i);
|
|
m_outputs.emplace_back(this, i, tensor_descriptor);
|
|
}
|
|
return m_outputs.at(position);
|
|
}
|
|
|
|
void Node::set_argument(size_t position, const Output<Node>& argument)
|
|
{
|
|
auto output_node = argument.get_node();
|
|
auto& output_descriptor = output_node->get_output_descriptor(argument.get_index());
|
|
get_input_descriptor(position).replace_output(output_descriptor);
|
|
}
|
|
|
|
void Node::constructor_validate_and_infer_types()
|
|
{
|
|
validate_and_infer_types();
|
|
}
|
|
|
|
void Node::set_output_size(size_t n)
|
|
{
|
|
NGRAPH_CHECK(n >= m_outputs.size(), "shrinking ", m_outputs.size(), " to ", n);
|
|
for (size_t i = m_outputs.size(); i < n; ++i)
|
|
{
|
|
// create the descriptors
|
|
get_output_descriptor(i);
|
|
}
|
|
}
|
|
|
|
void Node::invalidate_values()
|
|
{
|
|
for (const auto& output : outputs())
|
|
output.get_tensor().invalidate_values();
|
|
}
|
|
|
|
void Node::validate_and_infer_types()
|
|
{
|
|
}
|
|
|
|
void Node::set_input_is_relevant_to_shape(size_t i, bool relevant)
|
|
{
|
|
NGRAPH_CHECK(i < m_inputs.size(),
|
|
"index '",
|
|
i,
|
|
"' out of range in set_input_is_relevant_to_shape(size_t index, bool relevant)");
|
|
m_inputs[i].m_is_relevant_to_shape = relevant;
|
|
}
|
|
|
|
void Node::set_input_is_relevant_to_value(size_t i, bool relevant)
|
|
{
|
|
NGRAPH_CHECK(i < m_inputs.size(),
|
|
"index '",
|
|
i,
|
|
"' out of range in set_input_is_relevant_to_value(size_t index, bool relevant)");
|
|
m_inputs[i].m_is_relevant_to_value = relevant;
|
|
}
|
|
|
|
void Node::set_output_type(size_t i, const element::Type& element_type, const PartialShape& pshape)
|
|
{
|
|
get_output_descriptor(i).get_tensor_ptr()->set_tensor_type(element_type, pshape);
|
|
}
|
|
|
|
std::string Node::description() const
|
|
{
|
|
return get_type_name();
|
|
}
|
|
|
|
const std::string& Node::get_friendly_name() const
|
|
{
|
|
if (m_friendly_name.empty())
|
|
{
|
|
return get_name();
|
|
}
|
|
return m_friendly_name;
|
|
}
|
|
|
|
const std::string& Node::get_name() const
|
|
{
|
|
if (m_unique_name.empty())
|
|
{
|
|
const_cast<Node*>(this)->m_unique_name = description() + "_" + to_string(m_instance_id);
|
|
}
|
|
return m_unique_name;
|
|
}
|
|
|
|
void Node::set_friendly_name(const string& name)
|
|
{
|
|
m_friendly_name = name;
|
|
}
|
|
|
|
void Node::add_provenance_group_member(const shared_ptr<Node>& node)
|
|
{
|
|
m_provenance_group.insert(node);
|
|
}
|
|
|
|
void Node::remove_provenance_group_member(const shared_ptr<Node>& node)
|
|
{
|
|
m_provenance_group.erase(node);
|
|
}
|
|
|
|
void Node::replace_provenance_group_member(const shared_ptr<Node>& current_node,
|
|
const shared_ptr<Node>& replacement_node)
|
|
{
|
|
// Catch up with the current state of the group
|
|
replacement_node->add_provenance_tags(get_provenance_tags());
|
|
if (current_node != nullptr)
|
|
{
|
|
remove_provenance_group_member(current_node);
|
|
// Catch up with what was added to the current node
|
|
replacement_node->add_provenance_tags(current_node->get_provenance_tags());
|
|
}
|
|
add_provenance_group_member(replacement_node);
|
|
}
|
|
|
|
const set<shared_ptr<Node>>& Node::get_provenance_group_members() const
|
|
{
|
|
return m_provenance_group;
|
|
}
|
|
|
|
shared_ptr<Node> Node::add_provenance_group_members_above(const OutputVector& base)
|
|
{
|
|
set<Node*> base_set;
|
|
for (auto& output : base)
|
|
{
|
|
Node* node = output.get_node();
|
|
if (node == this)
|
|
{
|
|
// A builder did nothing
|
|
return shared_from_this();
|
|
}
|
|
base_set.insert(node);
|
|
}
|
|
vector<Node*> todo;
|
|
for (auto value : input_values())
|
|
{
|
|
todo.push_back(value.get_node());
|
|
}
|
|
while (!todo.empty())
|
|
{
|
|
Node* node = todo.back();
|
|
todo.pop_back();
|
|
if (base_set.count(node) > 0)
|
|
{
|
|
continue;
|
|
}
|
|
add_provenance_group_member(node->shared_from_this());
|
|
for (auto value : node->input_values())
|
|
{
|
|
if (m_provenance_group.count(value.get_node_shared_ptr()) == 0)
|
|
{
|
|
todo.push_back(value.get_node());
|
|
}
|
|
}
|
|
base_set.insert(node);
|
|
}
|
|
return shared_from_this();
|
|
}
|
|
|
|
void Node::add_provenance_tags_above(const OutputVector& base,
|
|
const std::unordered_set<std::string>& tag_set)
|
|
{
|
|
set<Node*> base_set;
|
|
for (auto& output : base)
|
|
{
|
|
base_set.insert(output.get_node());
|
|
}
|
|
vector<Node*> todo{this};
|
|
while (!todo.empty())
|
|
{
|
|
Node* node = todo.back();
|
|
todo.pop_back();
|
|
if (base_set.count(node) > 0)
|
|
{
|
|
continue;
|
|
}
|
|
node->add_provenance_tags(tag_set);
|
|
for (auto value : node->input_values())
|
|
{
|
|
todo.push_back(value.get_node());
|
|
}
|
|
base_set.insert(node);
|
|
}
|
|
}
|
|
|
|
const std::unordered_set<std::string>& Node::get_provenance_tags() const
|
|
{
|
|
return m_provenance_tags;
|
|
}
|
|
|
|
void Node::add_provenance_tag(const std::string& tag)
|
|
{
|
|
m_provenance_tags.insert(tag);
|
|
for (auto node : m_provenance_group)
|
|
{
|
|
node->add_provenance_tag(tag);
|
|
}
|
|
}
|
|
|
|
void Node::remove_provenance_tag(const std::string& tag)
|
|
{
|
|
m_provenance_tags.erase(tag);
|
|
}
|
|
|
|
void Node::merge_provenance_tags_from(const std::shared_ptr<const Node>& source)
|
|
{
|
|
for (auto& tag : source->get_provenance_tags())
|
|
{
|
|
add_provenance_tag(tag);
|
|
}
|
|
}
|
|
|
|
void Node::transfer_provenance_tags(const shared_ptr<Node>& replacement)
|
|
{
|
|
auto common_args = ngraph::find_common_args(shared_from_this(), replacement);
|
|
|
|
std::set<string> removed_subgraph_tags;
|
|
|
|
auto set_replacement_prov = [&removed_subgraph_tags](std::shared_ptr<Node> node) {
|
|
for (auto tag : node->get_provenance_tags())
|
|
{
|
|
removed_subgraph_tags.insert(tag);
|
|
}
|
|
};
|
|
|
|
traverse_nodes({shared_from_this()}, set_replacement_prov, common_args);
|
|
replacement->add_provenance_tags(removed_subgraph_tags);
|
|
|
|
auto set_prov_new_nodes = [&removed_subgraph_tags](std::shared_ptr<Node> node) {
|
|
node->add_provenance_tags(removed_subgraph_tags);
|
|
};
|
|
|
|
traverse_nodes({replacement}, set_prov_new_nodes, common_args);
|
|
}
|
|
|
|
Node* Node::get_input_node_ptr(size_t index) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
index < m_inputs.size(), "index '", index, "' out of range in get_argument(size_t index)");
|
|
return m_inputs[index].get_output().get_node().get();
|
|
}
|
|
|
|
std::shared_ptr<Node> Node::get_input_node_shared_ptr(size_t index) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
index < m_inputs.size(), "index '", index, "' out of range in get_argument(size_t index)");
|
|
return m_inputs[index].get_output().get_node();
|
|
}
|
|
|
|
Output<Node> Node::get_input_source_output(size_t i) const
|
|
{
|
|
return input(i).get_source_output();
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<Node>>& Node::get_control_dependencies() const
|
|
{
|
|
return m_control_dependencies;
|
|
}
|
|
|
|
const std::vector<Node*>& Node::get_control_dependents() const
|
|
{
|
|
return m_control_dependents;
|
|
}
|
|
|
|
void Node::add_control_dependency(std::shared_ptr<Node> node)
|
|
{
|
|
if (find(m_control_dependencies.begin(), m_control_dependencies.end(), node) ==
|
|
m_control_dependencies.end())
|
|
{
|
|
m_control_dependencies.push_back(node);
|
|
if (find(node->m_control_dependents.begin(), node->m_control_dependents.end(), this) ==
|
|
node->m_control_dependents.end())
|
|
{
|
|
node->m_control_dependents.push_back(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Node::add_node_control_dependencies(std::shared_ptr<Node> source_node)
|
|
{
|
|
for (auto& node : source_node->get_control_dependencies())
|
|
{
|
|
add_control_dependency(node);
|
|
}
|
|
}
|
|
|
|
void Node::add_node_control_dependents(std::shared_ptr<Node> source_node)
|
|
{
|
|
for (Node* node : source_node->get_control_dependents())
|
|
{
|
|
node->add_control_dependency(shared_from_this());
|
|
}
|
|
}
|
|
|
|
void Node::transfer_control_dependents(std::shared_ptr<Node> replacement)
|
|
{
|
|
replacement->add_node_control_dependents(shared_from_this());
|
|
clear_control_dependents();
|
|
}
|
|
|
|
void Node::remove_control_dependency(std::shared_ptr<Node> node)
|
|
{
|
|
{
|
|
auto it = find(m_control_dependencies.begin(), m_control_dependencies.end(), node);
|
|
if (it != m_control_dependencies.end())
|
|
{
|
|
m_control_dependencies.erase(it);
|
|
}
|
|
}
|
|
{
|
|
auto it = find(node->m_control_dependents.begin(), node->m_control_dependents.end(), this);
|
|
if (it != node->m_control_dependents.end())
|
|
{
|
|
node->m_control_dependents.erase(it);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Node::clear_control_dependencies()
|
|
{
|
|
for (auto& node : m_control_dependencies)
|
|
{
|
|
auto it = find(node->m_control_dependents.begin(), node->m_control_dependents.end(), this);
|
|
if (it != node->m_control_dependents.end())
|
|
{
|
|
node->m_control_dependents.erase(it);
|
|
}
|
|
}
|
|
m_control_dependencies.clear();
|
|
}
|
|
|
|
void Node::clear_control_dependents()
|
|
{
|
|
while (!m_control_dependents.empty())
|
|
{
|
|
(*m_control_dependents.begin())->remove_control_dependency(shared_from_this());
|
|
}
|
|
}
|
|
|
|
const op::AutoBroadcastSpec& Node::get_autob() const
|
|
{
|
|
static op::AutoBroadcastSpec s_spec;
|
|
return s_spec;
|
|
}
|
|
|
|
namespace ngraph
|
|
{
|
|
ostream& operator<<(ostream& out, const Node& node) { return node.write_description(out, 1); }
|
|
ostream& operator<<(ostream& out, const Node* node) { return node->write_description(out, 1); }
|
|
}
|
|
|
|
std::ostream& Node::write_description(std::ostream& out, uint32_t depth) const
|
|
{
|
|
if (depth == 0)
|
|
{
|
|
out << get_friendly_name();
|
|
}
|
|
else
|
|
{
|
|
out << "v" << get_type_info().version << "::" << get_type_info().name << " "
|
|
<< get_friendly_name() << " (";
|
|
string sep = "";
|
|
for (const auto& arg : input_values())
|
|
{
|
|
out << sep << arg;
|
|
sep = ", ";
|
|
}
|
|
out << ") -> (";
|
|
sep = "";
|
|
for (size_t i = 0; i < get_output_size(); i++)
|
|
{
|
|
out << sep << get_output_element_type(i) << get_output_partial_shape(i);
|
|
sep = ", ";
|
|
}
|
|
out << ")";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
size_t Node::get_output_size() const
|
|
{
|
|
return m_outputs.size();
|
|
}
|
|
|
|
const element::Type& Node::get_output_element_type(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
i < m_outputs.size(), "index '", i, "' out of range in get_output_element_type(size_t i)");
|
|
return m_outputs[i].get_element_type();
|
|
}
|
|
|
|
const element::Type& Node::get_element_type() const
|
|
{
|
|
if (get_output_size() != 1)
|
|
{
|
|
throw ngraph_error("get_element_type() must be called on a node with exactly one output.");
|
|
}
|
|
return get_output_element_type(0);
|
|
}
|
|
|
|
const Shape& Node::get_output_shape(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
i < m_outputs.size(), "index '", i, "' out of range in get_output_shape(size_t i)");
|
|
return m_outputs[i].get_shape();
|
|
}
|
|
|
|
const PartialShape& Node::get_output_partial_shape(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
i < m_outputs.size(), "index '", i, "' out of range in get_output_partial_shape(size_t i)");
|
|
return m_outputs[i].get_partial_shape();
|
|
}
|
|
|
|
const Shape& Node::get_shape() const
|
|
{
|
|
if (get_output_size() != 1)
|
|
{
|
|
stringstream es;
|
|
es << "get_shape() must be called on a node with exactly one output (" << description()
|
|
<< ")";
|
|
throw ngraph_error(es);
|
|
}
|
|
return get_output_shape(0);
|
|
}
|
|
|
|
std::set<Input<Node>> Node::get_output_target_inputs(size_t i) const
|
|
{
|
|
std::set<Input<Node>> result;
|
|
|
|
for (auto& input : m_outputs.at(i).get_inputs())
|
|
{
|
|
result.emplace(input->get_raw_pointer_node(), input->get_index());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
descriptor::Tensor& Node::get_output_tensor(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
i < m_outputs.size(), "index '", i, "' out of range in get_output_tensor(size_t i)");
|
|
return m_outputs[i].get_tensor();
|
|
}
|
|
|
|
descriptor::Tensor& Node::get_input_tensor(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(i < m_inputs.size(), "index '", i, "' out of range in get_input_tensor(size_t i)");
|
|
descriptor::Input input = m_inputs[i];
|
|
return input.get_tensor();
|
|
}
|
|
|
|
size_t Node::get_input_size() const
|
|
{
|
|
return m_inputs.size();
|
|
}
|
|
|
|
const element::Type& Node::get_input_element_type(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
i < m_inputs.size(), "index '", i, "' out of range in get_input_element_type(size_t i)");
|
|
return m_inputs[i].get_element_type();
|
|
}
|
|
|
|
const Shape& Node::get_input_shape(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(i < m_inputs.size(), "index '", i, "' out of range in get_input_shape(size_t i)");
|
|
return m_inputs[i].get_shape();
|
|
}
|
|
|
|
const PartialShape& Node::get_input_partial_shape(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
i < m_inputs.size(), "index '", i, "' out of range in get_input_partial_shape(size_t i)");
|
|
return m_inputs[i].get_partial_shape();
|
|
}
|
|
|
|
NGRAPH_SUPPRESS_DEPRECATED_START
|
|
const string& Node::get_input_tensor_name(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
i < m_inputs.size(), "index '", i, "' out of range in get_input_tensor_name(size_t i)");
|
|
return m_inputs[i].get_tensor().get_name();
|
|
}
|
|
|
|
const string& Node::get_output_tensor_name(size_t i) const
|
|
{
|
|
NGRAPH_CHECK(
|
|
i < m_outputs.size(), "index '", i, "' out of range in get_output_tensor_name(size_t i)");
|
|
return m_outputs[i].get_tensor().get_name();
|
|
}
|
|
NGRAPH_SUPPRESS_DEPRECATED_END
|
|
|
|
bool Node::has_same_type(std::shared_ptr<const Node> node) const
|
|
{
|
|
if (get_output_size() != node->get_output_size())
|
|
{
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < get_output_size(); ++i)
|
|
{
|
|
if (get_output_element_type(i) != node->get_output_element_type(i) ||
|
|
get_output_shape(i) != node->get_output_shape(i))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
NodeVector Node::get_users(bool check_is_used) const
|
|
{
|
|
NodeVector result;
|
|
for (auto output : outputs())
|
|
{
|
|
for (auto input : output.get_target_inputs())
|
|
{
|
|
Node* input_node = input.get_node();
|
|
if (!check_is_used || is_used(input_node))
|
|
{
|
|
result.push_back(input_node->shared_from_this());
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string ngraph::node_validation_failure_loc_string(const Node* node)
|
|
{
|
|
std::stringstream ss;
|
|
ss << "While validating node '" << *node << "' with friendly_name '"
|
|
<< node->get_friendly_name() << '\'';
|
|
return ss.str();
|
|
}
|
|
|
|
const std::shared_ptr<Node>& ngraph::check_single_output_arg(const std::shared_ptr<Node>& node,
|
|
size_t i)
|
|
{
|
|
NGRAPH_CHECK(
|
|
node->get_output_size() == 1, "Argument ", i, node, " must produce exactly one value.");
|
|
return node;
|
|
}
|
|
|
|
const NodeVector& ngraph::check_single_output_args(const NodeVector& args)
|
|
{
|
|
for (size_t i = 0; i < args.size(); ++i)
|
|
{
|
|
ngraph::check_single_output_arg(args.at(i), i);
|
|
}
|
|
return args;
|
|
}
|
|
|
|
OutputVector ngraph::as_output_vector(const NodeVector& args)
|
|
{
|
|
OutputVector output_vector;
|
|
for (auto arg : args)
|
|
{
|
|
output_vector.push_back(arg);
|
|
}
|
|
return output_vector;
|
|
}
|
|
|
|
NodeVector ngraph::as_node_vector(const OutputVector& values)
|
|
{
|
|
NodeVector node_vector;
|
|
for (auto& value : values)
|
|
{
|
|
node_vector.emplace_back(value.get_node_shared_ptr());
|
|
}
|
|
return node_vector;
|
|
}
|
|
|
|
ResultVector ngraph::as_result_vector(const OutputVector& values)
|
|
{
|
|
ResultVector result;
|
|
for (auto value : values)
|
|
{
|
|
shared_ptr<Node> node = value.get_node_shared_ptr();
|
|
result.push_back(is_type<op::Result>(node) ? as_type_ptr<op::Result>(node)
|
|
: make_shared<op::Result>(value));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Node::match_value(pattern::Matcher* matcher,
|
|
const Output<Node>& pattern_value,
|
|
const Output<Node>& graph_value)
|
|
{
|
|
if (pattern_value.get_index() != graph_value.get_index() ||
|
|
(matcher->is_strict_mode() &&
|
|
(!pattern_value.get_element_type().compatible(graph_value.get_element_type()) ||
|
|
!pattern_value.get_partial_shape().compatible(graph_value.get_partial_shape()))))
|
|
{
|
|
return false;
|
|
}
|
|
return match_node(matcher, graph_value);
|
|
}
|
|
|
|
bool Node::match_node(pattern::Matcher* matcher, const Output<Node>& graph_value)
|
|
{
|
|
matcher->add_node(graph_value);
|
|
// Check if a type of a given node, which produces graph_value, matches the type of `this` node
|
|
// or `this` node type is an ancestor of that node type. It is not the exact matching, types of
|
|
// the nodes
|
|
// may not match, but they are connected by the inheritance relation.
|
|
// Not exact matching allows using base classes in the patterns and successfully matching such
|
|
// patterns
|
|
// with sub-graph of descent nodes types.
|
|
if (graph_value.get_node_shared_ptr()->get_type_info().is_castable(get_type_info()) &&
|
|
matcher->match_arguments(this, graph_value.get_node_shared_ptr()))
|
|
{
|
|
auto& pattern_map = matcher->get_pattern_value_map();
|
|
pattern_map[shared_from_this()] = graph_value;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// default implementation for the node to check if it contains partial shape
|
|
// we will override this method, for the Op's which depends on additional shape
|
|
// attribute to determine if node contains partial shape or not
|
|
bool Node::is_dynamic() const
|
|
{
|
|
for (size_t i = 0; i < get_input_size(); i++)
|
|
{
|
|
if (get_input_partial_shape(i).is_dynamic())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Input<Node> Node::input(size_t input_index)
|
|
{
|
|
if (input_index >= m_inputs.size())
|
|
{
|
|
throw out_of_range("node input index is out of range");
|
|
}
|
|
|
|
return Input<Node>(this, input_index);
|
|
}
|
|
|
|
Output<Node> Node::input_value(size_t input_index) const
|
|
{
|
|
return input(input_index).get_source_output();
|
|
}
|
|
|
|
Input<const Node> Node::input(size_t input_index) const
|
|
{
|
|
if (input_index >= m_inputs.size())
|
|
{
|
|
throw out_of_range("node input index is out of range");
|
|
}
|
|
|
|
return Input<const Node>(this, input_index);
|
|
}
|
|
|
|
Output<Node> Node::output(size_t output_index)
|
|
{
|
|
// All nodes will have at least 1 output
|
|
if (output_index > 0 && output_index >= m_outputs.size())
|
|
{
|
|
throw out_of_range("node output index is out of range");
|
|
}
|
|
|
|
return Output<Node>(this, output_index);
|
|
}
|
|
|
|
Output<const Node> Node::output(size_t output_index) const
|
|
{
|
|
// All nodes will have at least 1 output
|
|
if (output_index > 0 && output_index >= m_outputs.size())
|
|
{
|
|
throw out_of_range("node output index is out of range");
|
|
}
|
|
|
|
return Output<const Node>(this, output_index);
|
|
}
|
|
|
|
vector<Input<Node>> Node::inputs()
|
|
{
|
|
vector<Input<Node>> result;
|
|
|
|
for (size_t i = 0; i < get_input_size(); i++)
|
|
{
|
|
result.emplace_back(this, i);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<Output<Node>> Node::input_values() const
|
|
{
|
|
vector<Output<Node>> result;
|
|
|
|
for (size_t i = 0; i < get_input_size(); i++)
|
|
{
|
|
result.emplace_back(input(i).get_source_output());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<Input<const Node>> Node::inputs() const
|
|
{
|
|
vector<Input<const Node>> result;
|
|
|
|
for (size_t i = 0; i < get_input_size(); i++)
|
|
{
|
|
result.emplace_back(this, i);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<Output<Node>> Node::outputs()
|
|
{
|
|
vector<Output<Node>> result;
|
|
|
|
for (size_t i = 0; i < get_output_size(); i++)
|
|
{
|
|
result.emplace_back(shared_from_this(), i);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
vector<Output<const Node>> Node::outputs() const
|
|
{
|
|
vector<Output<const Node>> result;
|
|
|
|
for (size_t i = 0; i < get_output_size(); i++)
|
|
{
|
|
result.emplace_back(shared_from_this(), i);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool Node::evaluate(const HostTensorVector& output_values,
|
|
const HostTensorVector& input_values) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool Node::evaluate_lower(const HostTensorVector& output_values) const
|
|
{
|
|
const auto& inputs = input_values();
|
|
bool dyn_inputs = std::any_of(inputs.begin(), inputs.end(), [](const Output<Node>& output) {
|
|
return !output.get_tensor().has_and_set_bound();
|
|
});
|
|
if (dyn_inputs)
|
|
return false;
|
|
return default_lower_bound_evaluator(this, output_values);
|
|
}
|
|
|
|
bool Node::evaluate_upper(const HostTensorVector& output_values) const
|
|
{
|
|
const auto& inputs = input_values();
|
|
bool dyn_inputs = std::any_of(inputs.begin(), inputs.end(), [](const Output<Node>& output) {
|
|
return !output.get_tensor().has_and_set_bound();
|
|
});
|
|
if (dyn_inputs)
|
|
return false;
|
|
return default_upper_bound_evaluator(this, output_values);
|
|
}
|
|
|
|
bool Node::constant_fold(OutputVector& output_values, const OutputVector& input_values)
|
|
{
|
|
OV_ITT_SCOPED_TASK(itt::domains::nGraph, "Node::constant_fold");
|
|
|
|
if (m_rt_info.count("DISABLED_CONSTANT_FOLDING"))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If all the inputs are constants, try to evaluate the outputs
|
|
bool all_constants =
|
|
std::all_of(input_values.begin(), input_values.end(), [](const Output<Node>& input) {
|
|
return as_type_ptr<op::v0::Constant>(input.get_node_shared_ptr());
|
|
});
|
|
if (!all_constants)
|
|
return false;
|
|
|
|
HostTensorVector input_tensors;
|
|
for (const auto& input : input_values)
|
|
{
|
|
auto host_tensor = make_shared<runtime::HostTensor>(
|
|
as_type_ptr<op::v0::Constant>(input.get_node_shared_ptr()));
|
|
input_tensors.push_back(host_tensor);
|
|
}
|
|
HostTensorVector output_tensors;
|
|
OutputVector output_constants;
|
|
for (const auto& output : outputs())
|
|
{
|
|
auto tensor =
|
|
make_shared<HostTensor>(output.get_element_type(), output.get_partial_shape());
|
|
output_tensors.push_back(tensor);
|
|
}
|
|
if (evaluate(output_tensors, input_tensors))
|
|
{
|
|
for (size_t i = 0; i < output_tensors.size(); ++i)
|
|
{
|
|
output_values[i] = make_shared<op::Constant>(output_tensors[i]);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
constexpr DiscreteTypeInfo AttributeAdapter<shared_ptr<Node>>::type_info;
|
|
|
|
AttributeAdapter<std::shared_ptr<Node>>::AttributeAdapter(std::shared_ptr<Node>& value)
|
|
: m_ref(value)
|
|
{
|
|
}
|
|
|
|
bool AttributeAdapter<std::shared_ptr<Node>>::visit_attributes(AttributeVisitor& visitor)
|
|
{
|
|
auto original_id = visitor.get_registered_node_id(m_ref);
|
|
auto id = original_id;
|
|
visitor.on_attribute("ID", id);
|
|
if (id != original_id)
|
|
{
|
|
m_ref = visitor.get_registered_node(id);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
constexpr DiscreteTypeInfo AttributeAdapter<NodeVector>::type_info;
|
|
|
|
AttributeAdapter<NodeVector>::AttributeAdapter(NodeVector& ref)
|
|
: m_ref(ref)
|
|
{
|
|
}
|
|
|
|
bool AttributeAdapter<NodeVector>::visit_attributes(AttributeVisitor& visitor)
|
|
{
|
|
int64_t size = m_ref.size();
|
|
visitor.on_attribute("size", size);
|
|
if (size != m_ref.size())
|
|
{
|
|
m_ref.resize(size);
|
|
}
|
|
ostringstream index;
|
|
for (int64_t i = 0; i < size; i++)
|
|
{
|
|
index.str("");
|
|
index << i;
|
|
string id;
|
|
if (m_ref[i])
|
|
{
|
|
id = visitor.get_registered_node_id(m_ref[i]);
|
|
}
|
|
visitor.on_attribute(index.str(), id);
|
|
if (!m_ref[i])
|
|
{
|
|
m_ref[i] = visitor.get_registered_node(id);
|
|
}
|
|
}
|
|
return true;
|
|
}
|