202 lines
7.1 KiB
C++
202 lines
7.1 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 <cstddef>
|
|
#include <functional>
|
|
#include <set>
|
|
|
|
#include "itt.hpp"
|
|
#include "ngraph/builder/reshape.hpp"
|
|
#include "ngraph/op/constant.hpp"
|
|
#include "ngraph/op/reshape.hpp"
|
|
#include "ngraph/op/unsqueeze.hpp"
|
|
#include "ngraph/op/util/op_types.hpp"
|
|
#include "ngraph/runtime/reference/copy.hpp"
|
|
#include "ngraph/validation_util.hpp"
|
|
|
|
using namespace std;
|
|
using namespace ngraph;
|
|
|
|
NGRAPH_RTTI_DEFINITION(op::v0::Unsqueeze, "Unsqueeze", 0);
|
|
|
|
op::v0::Unsqueeze::Unsqueeze(const Output<Node>& data, const Output<Node>& axes)
|
|
: Op({data, axes})
|
|
{
|
|
constructor_validate_and_infer_types();
|
|
}
|
|
|
|
void op::v0::Unsqueeze::validate_and_infer_types()
|
|
{
|
|
NGRAPH_OP_SCOPE(v0_Unsqueeze_validate_and_infer_types);
|
|
const auto data = input_value(0);
|
|
auto data_partial_shape = data.get_partial_shape();
|
|
const auto data_rank = data_partial_shape.rank();
|
|
|
|
const auto axes_constant = get_constant_from_source(input_value(1));
|
|
if (data_rank.is_dynamic() || !axes_constant)
|
|
{
|
|
set_output_type(0, get_input_element_type(0), PartialShape::dynamic());
|
|
return;
|
|
}
|
|
|
|
uint64_t data_rank_value = data_partial_shape.rank().get_length();
|
|
|
|
// Get value of axes from Constant
|
|
const auto axes_values = axes_constant->cast_vector<int64_t>();
|
|
const auto expanded_rank = data_rank_value + axes_values.size();
|
|
auto axes = normalize_axes(this->description(), axes_values, expanded_rank);
|
|
|
|
NODE_VALIDATION_CHECK(this, !axes.empty(), "'axes' input is mandatory.");
|
|
NODE_VALIDATION_CHECK(this,
|
|
axes.size() == set<int64_t>(begin(axes), end(axes)).size(),
|
|
"'axes' input has a duplicate axis.");
|
|
|
|
sort(begin(axes), end(axes), less<int64_t>());
|
|
|
|
vector<Dimension> output_shape{data_partial_shape};
|
|
for (auto axis : axes)
|
|
{
|
|
NODE_VALIDATION_CHECK(
|
|
this, axis <= expanded_rank, "provided 'axes' value ", axis, " is not valid.");
|
|
|
|
output_shape.insert(next(begin(output_shape), axis), 1);
|
|
}
|
|
set_output_type(0, get_input_element_type(0), PartialShape{output_shape});
|
|
}
|
|
|
|
bool op::v0::Unsqueeze::visit_attributes(AttributeVisitor& visitor)
|
|
{
|
|
NGRAPH_OP_SCOPE(v0_Unsqueeze_visit_attributes);
|
|
return true;
|
|
}
|
|
|
|
shared_ptr<Node> op::v0::Unsqueeze::clone_with_new_inputs(const OutputVector& new_args) const
|
|
{
|
|
NGRAPH_OP_SCOPE(v0_Unsqueeze_clone_with_new_inputs);
|
|
if (new_args.size() != 2)
|
|
{
|
|
throw ngraph_error("Incorrect number of new arguments");
|
|
}
|
|
return make_shared<Unsqueeze>(new_args.at(0), new_args.at(1));
|
|
}
|
|
|
|
namespace unsqueeze
|
|
{
|
|
template <element::Type_t ET>
|
|
bool evaluate(const HostTensorPtr& arg0, const HostTensorPtr& out)
|
|
{
|
|
runtime::reference::copy(
|
|
arg0->get_data_ptr<ET>(), out->get_data_ptr<ET>(), shape_size(out->get_shape()));
|
|
return true;
|
|
}
|
|
|
|
bool evaluate_unsqueeze(const HostTensorPtr& arg0,
|
|
const HostTensorPtr& arg1,
|
|
const HostTensorPtr& out)
|
|
{
|
|
auto element_type = arg0->get_element_type();
|
|
out->set_element_type(element_type);
|
|
|
|
auto data_shape = arg0->get_shape();
|
|
int64_t data_rank = static_cast<int64_t>(data_shape.size());
|
|
auto axes_shape = arg1->get_shape();
|
|
NGRAPH_CHECK(axes_shape.size() == 1, "Axes to add must be a vector.");
|
|
NGRAPH_CHECK(axes_shape[0] > 0, "Axes cannot be empty.");
|
|
|
|
auto out_shape = data_shape;
|
|
int64_t out_rank = data_rank + static_cast<int64_t>(shape_size(axes_shape));
|
|
// Get axes
|
|
vector<int64_t> axes = read_index_vector(arg1);
|
|
// Normalize axes
|
|
std::transform(axes.begin(), axes.end(), axes.begin(), [out_rank](int64_t i) -> int64_t {
|
|
return i < 0 ? out_rank + i : i;
|
|
});
|
|
// Sort in increasing order
|
|
std::set<int64_t, less<int64_t>> axes_set(axes.begin(), axes.end());
|
|
NGRAPH_CHECK(axes.size() == axes_set.size(), "Axes has duplicate axis.");
|
|
for (int64_t axis : axes_set)
|
|
{
|
|
NGRAPH_CHECK(axis >= 0 && axis < out_rank, "Axis is out of bounds: ", axis);
|
|
out_shape.insert(out_shape.begin() + axis, 1);
|
|
}
|
|
out->set_shape(out_shape);
|
|
|
|
bool rc = true;
|
|
switch (element_type)
|
|
{
|
|
NGRAPH_TYPE_CASE(evaluate_unsqueeze, i32, arg0, out);
|
|
NGRAPH_TYPE_CASE(evaluate_unsqueeze, i64, arg0, out);
|
|
NGRAPH_TYPE_CASE(evaluate_unsqueeze, u32, arg0, out);
|
|
NGRAPH_TYPE_CASE(evaluate_unsqueeze, u64, arg0, out);
|
|
NGRAPH_TYPE_CASE(evaluate_unsqueeze, f16, arg0, out);
|
|
NGRAPH_TYPE_CASE(evaluate_unsqueeze, f32, arg0, out);
|
|
default: rc = false; break;
|
|
}
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
bool op::v0::Unsqueeze::evaluate(const HostTensorVector& outputs,
|
|
const HostTensorVector& inputs) const
|
|
{
|
|
NGRAPH_OP_SCOPE(v0_Unsqueeze_evaluate);
|
|
return unsqueeze::evaluate_unsqueeze(inputs[0], inputs[1], outputs[0]);
|
|
}
|
|
|
|
bool op::v0::Unsqueeze::evaluate_lower(const HostTensorVector& output_values) const
|
|
{
|
|
if (!input_value(1).get_tensor().has_and_set_bound())
|
|
return false;
|
|
return default_lower_bound_evaluator(this, output_values);
|
|
}
|
|
|
|
bool op::v0::Unsqueeze::evaluate_upper(const HostTensorVector& output_values) const
|
|
{
|
|
if (!input_value(1).get_tensor().has_and_set_bound())
|
|
return false;
|
|
return default_upper_bound_evaluator(this, output_values);
|
|
}
|
|
|
|
bool op::v0::Unsqueeze::constant_fold(OutputVector& output_values,
|
|
const OutputVector& inputs_values)
|
|
{
|
|
if (get_output_partial_shape(0).is_dynamic())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto& shape = get_output_shape(0);
|
|
|
|
if (auto data_const =
|
|
std::dynamic_pointer_cast<op::Constant>(inputs_values[0].get_node_shared_ptr()))
|
|
{
|
|
// In case if data constant has single consumer we can change it shape without making a copy
|
|
// Otherwise we create Constant copy with shape from unsqueeze node
|
|
if (data_const->output(0).get_target_inputs().size() == 1)
|
|
{
|
|
data_const->set_data_shape(shape);
|
|
data_const->validate_and_infer_types();
|
|
output_values[0] = data_const;
|
|
}
|
|
else
|
|
{
|
|
output_values[0] = std::make_shared<op::Constant>(
|
|
data_const->get_element_type(), shape, data_const->get_data_ptr());
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|