openvino/ngraph/core/src/op/constant.cpp

682 lines
22 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 <cmath>
#include <cstdio>
#include <cstring>
#include <ngraph/validation_util.hpp>
#include "itt.hpp"
#include "ngraph/log.hpp"
#include "ngraph/op/constant.hpp"
#include "ngraph/op/util/attr_types.hpp"
#include "ngraph/util.hpp"
using namespace ngraph;
using namespace std;
template <typename T>
string to_cpp_string(T value)
{
string rc;
if (std::isnan(value))
{
rc = "NAN";
}
else if (std::isinf(value))
{
rc = (value > 0 ? "INFINITY" : "-INFINITY");
}
else
{
stringstream ss;
ss << value;
rc = ss.str();
}
return rc;
}
constexpr NodeTypeInfo op::Constant::type_info;
op::Constant::Constant(const shared_ptr<runtime::Tensor>& tensor)
: Constant(tensor->get_element_type(), tensor->get_shape())
{
tensor->read(get_data_ptr_nc(), tensor->get_size_in_bytes());
m_all_elements_bitwise_identical = are_all_data_elements_bitwise_identical();
}
op::Constant::Constant(const element::Type& type,
Shape shape,
const std::vector<std::string>& values)
: Constant(type, shape)
{
NODE_VALIDATION_CHECK(this,
values.size() == shape_size(m_shape) || values.size() == 1,
"Did not get the expected number of literals for a constant of shape ",
m_shape,
" (got ",
values.size(),
", expected ",
shape_size(m_shape),
".");
constructor_validate_and_infer_types();
if (values.size() == 1 && shape_size(m_shape) != 1)
{
// broadcast single value
switch (m_element_type)
{
case element::Type_t::boolean:
{
bool value = stoi(values[0]) != 0;
auto target = get_data_ptr_nc<element::Type_t::boolean>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::bf16:
{
bfloat16 value = parse_string<float>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::bf16>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::f16:
{
float16 value = parse_string<float>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::f16>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::f32:
{
float value = parse_string<float>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::f32>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::f64:
{
double value = parse_string<double>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::f64>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::i8:
{
int8_t value = parse_string<int64_t>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::i8>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::i16:
{
int16_t value = parse_string<int64_t>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::i16>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::i32:
{
int32_t value = parse_string<int64_t>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::i32>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::i64:
{
int64_t value = parse_string<int64_t>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::i64>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::u8:
{
uint8_t value = parse_string<uint64_t>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::u8>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::u16:
{
uint16_t value = parse_string<uint64_t>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::u16>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::u32:
{
uint32_t value = parse_string<uint64_t>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::u32>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::u64:
{
uint64_t value = parse_string<uint64_t>(values[0]);
auto target = get_data_ptr_nc<element::Type_t::u64>();
std::fill(target, target + shape_size(m_shape), value);
break;
}
case element::Type_t::undefined:
{
throw std::runtime_error("deserialize unsupported type undefined");
}
case element::Type_t::dynamic:
{
throw std::runtime_error("deserialize unsupported type dynamic");
}
case element::Type_t::u1: { throw std::runtime_error("deserialize unsupported type u1");
}
}
m_all_elements_bitwise_identical = true;
}
else
{
switch (m_element_type)
{
case element::Type_t::boolean:
{
vector<uint8_t> value = parse_string<uint8_t>(values);
auto target = get_data_ptr_nc<element::Type_t::boolean>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::bf16:
{
vector<float> value = parse_string<float>(values);
auto target = get_data_ptr_nc<element::Type_t::bf16>();
for (size_t i = 0; i < value.size(); i++)
{
target[i] = value[i];
}
break;
}
case element::Type_t::f16:
{
vector<float> value = parse_string<float>(values);
auto target = get_data_ptr_nc<element::Type_t::f16>();
for (size_t i = 0; i < value.size(); i++)
{
target[i] = value[i];
}
break;
}
case element::Type_t::f32:
{
vector<float> value = parse_string<float>(values);
auto target = get_data_ptr_nc<element::Type_t::f32>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::f64:
{
vector<double> value = parse_string<double>(values);
auto target = get_data_ptr_nc<element::Type_t::f64>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::i8:
{
vector<int8_t> value = parse_string<int8_t>(values);
auto target = get_data_ptr_nc<element::Type_t::i8>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::i16:
{
vector<int16_t> value = parse_string<int16_t>(values);
auto target = get_data_ptr_nc<element::Type_t::i16>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::i32:
{
vector<int32_t> value = parse_string<int32_t>(values);
auto target = get_data_ptr_nc<element::Type_t::i32>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::i64:
{
vector<int64_t> value = parse_string<int64_t>(values);
auto target = get_data_ptr_nc<element::Type_t::i64>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::u8:
{
vector<uint8_t> value = parse_string<uint8_t>(values);
auto target = get_data_ptr_nc<element::Type_t::u8>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::u16:
{
vector<uint16_t> value = parse_string<uint16_t>(values);
auto target = get_data_ptr_nc<element::Type_t::u16>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::u32:
{
vector<uint32_t> value = parse_string<uint32_t>(values);
auto target = get_data_ptr_nc<element::Type_t::u32>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::u64:
{
vector<uint64_t> value = parse_string<uint64_t>(values);
auto target = get_data_ptr_nc<element::Type_t::u64>();
std::copy(value.begin(), value.end(), target);
break;
}
case element::Type_t::undefined:
throw std::runtime_error("deserialize unsupported type undefined");
case element::Type_t::dynamic:
throw std::runtime_error("deserialize unsupported type dynamic");
case element::Type_t::u1: throw std::runtime_error("deserialize unsupported type u1");
}
m_all_elements_bitwise_identical = are_all_data_elements_bitwise_identical();
}
}
op::Constant::Constant(const element::Type& type, const Shape& shape)
: m_element_type(type)
, m_shape(shape)
{
allocate_buffer();
constructor_validate_and_infer_types();
}
void op::Constant::allocate_buffer()
{
m_data = make_shared<runtime::AlignedBuffer>(shape_size(m_shape) * m_element_type.size(),
host_alignment());
std::memset(m_data->get_ptr(), 0, m_data->size());
}
op::Constant::Constant(const element::Type& type, const Shape& shape, const void* data)
: Constant(type, shape)
{
size_t size = ceil(shape_size(m_shape) * m_element_type.bitwidth() / 8.f);
std::memcpy(get_data_ptr_nc(), data, size);
constructor_validate_and_infer_types();
m_all_elements_bitwise_identical = are_all_data_elements_bitwise_identical();
}
op::Constant::Constant(const Constant& other)
{
m_element_type = other.m_element_type;
m_shape = other.m_shape;
m_data = other.m_data;
m_all_elements_bitwise_identical = other.m_all_elements_bitwise_identical;
constructor_validate_and_infer_types();
}
op::Constant::~Constant()
{
}
string op::Constant::convert_value_to_string(size_t index) const
{
string rc;
#if defined(__GNUC__) && !(__GNUC__ == 4 && __GNUC_MINOR__ == 8)
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
#pragma GCC diagnostic error "-Wswitch-enum"
#endif
switch (get_element_type())
{
case element::Type_t::boolean: rc = to_string(get_data_ptr<char>()[index]); break;
case element::Type_t::bf16:
rc = to_cpp_string(static_cast<float>(get_data_ptr<bfloat16>()[index]));
break;
case element::Type_t::f16:
rc = to_cpp_string(static_cast<float>(get_data_ptr<float16>()[index]));
break;
case element::Type_t::f32: rc = to_cpp_string(get_data_ptr<float>()[index]); break;
case element::Type_t::f64: rc = to_cpp_string(get_data_ptr<double>()[index]); break;
case element::Type_t::i8: rc = to_string(get_data_ptr<int8_t>()[index]); break;
case element::Type_t::i16: rc = to_string(get_data_ptr<int16_t>()[index]); break;
case element::Type_t::i32: rc = to_string(get_data_ptr<int32_t>()[index]); break;
case element::Type_t::i64: rc = to_string(get_data_ptr<int64_t>()[index]); break;
case element::Type_t::u1:
rc = to_string((get_data_ptr<uint8_t>()[index / 8] >> (7 - (index % 8))) & 1);
break;
case element::Type_t::u8: rc = to_string(get_data_ptr<uint8_t>()[index]); break;
case element::Type_t::u16: rc = to_string(get_data_ptr<uint16_t>()[index]); break;
case element::Type_t::u32: rc = to_string(get_data_ptr<uint32_t>()[index]); break;
case element::Type_t::u64: rc = to_string(get_data_ptr<uint64_t>()[index]); break;
case element::Type_t::undefined: throw runtime_error("unsupported type");
case element::Type_t::dynamic: throw runtime_error("unsupported type");
}
#if defined(__GNUC__) && !(__GNUC__ == 4 && __GNUC_MINOR__ == 8)
#pragma GCC diagnostic pop
#endif
return rc;
}
vector<string> op::Constant::get_value_strings() const
{
vector<string> rc;
#if defined(__GNUC__) && !(__GNUC__ == 4 && __GNUC_MINOR__ == 8)
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
#pragma GCC diagnostic error "-Wswitch-enum"
#endif
switch (get_element_type())
{
case element::Type_t::boolean:
for (int value : get_vector<char>())
{
rc.push_back(to_string(value));
}
break;
case element::Type_t::bf16:
for (bfloat16 value : get_vector<bfloat16>())
{
rc.push_back(to_cpp_string(static_cast<float>(value)));
}
break;
case element::Type_t::f16:
for (float16 value : get_vector<float16>())
{
rc.push_back(to_cpp_string(static_cast<float>(value)));
}
break;
case element::Type_t::f32:
for (float value : get_vector<float>())
{
rc.push_back(to_cpp_string(value));
}
break;
case element::Type_t::f64:
for (double value : get_vector<double>())
{
rc.push_back(to_cpp_string(value));
}
break;
case element::Type_t::i8:
for (int value : get_vector<int8_t>())
{
rc.push_back(to_string(value));
}
break;
case element::Type_t::i16:
for (int value : get_vector<int16_t>())
{
rc.push_back(to_string(value));
}
break;
case element::Type_t::i32:
for (int32_t value : get_vector<int32_t>())
{
rc.push_back(to_string(value));
}
break;
case element::Type_t::i64:
for (int64_t value : get_vector<int64_t>())
{
rc.push_back(to_string(value));
}
break;
case element::Type_t::u8:
for (uint32_t value : get_vector<uint8_t>())
{
rc.push_back(to_string(value));
}
break;
case element::Type_t::u16:
for (uint32_t value : get_vector<uint16_t>())
{
rc.push_back(to_string(value));
}
break;
case element::Type_t::u32:
for (uint32_t value : get_vector<uint32_t>())
{
rc.push_back(to_string(value));
}
break;
case element::Type_t::u64:
for (uint64_t value : get_vector<uint64_t>())
{
rc.push_back(to_string(value));
}
break;
case element::Type_t::u1: throw runtime_error("unsupported type");
case element::Type_t::undefined: throw runtime_error("unsupported type");
case element::Type_t::dynamic: throw runtime_error("unsupported type");
}
#if defined(__GNUC__) && !(__GNUC__ == 4 && __GNUC_MINOR__ == 8)
#pragma GCC diagnostic pop
#endif
return rc;
}
Shape op::Constant::get_shape_val() const
{
NGRAPH_CHECK(m_element_type.is_integral_number());
std::vector<int64_t> out_shape = cast_vector<int64_t>();
Shape output_shape(shape_size(m_shape));
std::transform(out_shape.begin(), out_shape.end(), output_shape.begin(), [&](const int64_t& v) {
return (v > 0) ? v : 0;
});
return output_shape;
}
Strides op::Constant::get_strides_val() const
{
NGRAPH_CHECK(m_element_type == element::i64);
std::vector<int64_t> out_strides = cast_vector<int64_t>();
Strides output_strides(shape_size(m_shape));
std::transform(out_strides.begin(),
out_strides.end(),
output_strides.begin(),
[&](const int64_t& v) { return (v > 0) ? v : 0; });
return output_strides;
}
Coordinate op::Constant::get_coordinate_val() const
{
NGRAPH_CHECK(m_element_type == element::i64);
std::vector<int64_t> out_coordinate = cast_vector<int64_t>();
Coordinate output_coordinate(shape_size(m_shape));
std::transform(out_coordinate.begin(),
out_coordinate.end(),
output_coordinate.begin(),
[&](const int64_t& v) { return (v > 0) ? v : 0; });
return output_coordinate;
}
CoordinateDiff op::Constant::get_coordinate_diff_val() const
{
NGRAPH_CHECK(m_element_type == element::i64);
std::vector<int64_t> out_coordinate_diff = cast_vector<int64_t>();
CoordinateDiff output_coordinate_diff(shape_size(m_shape));
std::transform(out_coordinate_diff.begin(),
out_coordinate_diff.end(),
output_coordinate_diff.begin(),
[&](const int64_t& v) { return (v > 0) ? v : 0; });
return output_coordinate_diff;
}
AxisVector op::Constant::get_axis_vector_val() const
{
NGRAPH_CHECK(m_element_type.is_integral_number());
std::vector<int64_t> out_axis_vector = cast_vector<int64_t>();
AxisVector output_axis_vector(shape_size(m_shape));
std::transform(out_axis_vector.begin(),
out_axis_vector.end(),
output_axis_vector.begin(),
[&](const int64_t& v) { return (v > 0) ? v : 0; });
return output_axis_vector;
}
AxisSet op::Constant::get_axis_set_val() const
{
NGRAPH_CHECK(m_element_type.is_integral_number());
std::vector<int64_t> out_axis_set = cast_vector<int64_t>();
AxisSet output_axis_set;
for (auto& axis : out_axis_set)
{
output_axis_set.insert(axis > 0 ? axis : 0);
}
return output_axis_set;
}
void op::Constant::set_data_shape(const Shape& shape)
{
NGRAPH_CHECK(shape_size(shape) == shape_size(m_shape));
m_shape = shape;
}
shared_ptr<Node> op::Constant::clone_with_new_inputs(const OutputVector& new_args) const
{
NGRAPH_OP_SCOPE(v0_Constant_clone_with_new_inputs);
check_new_args_count(this, new_args);
return make_shared<Constant>(*this);
}
template <typename T>
static bool test_bitwise_identical(const op::Constant* constant)
{
const size_t size = shape_size(constant->get_shape());
bool data_is_constant = true;
if (size > 0)
{
const T* data = constant->get_data_ptr<T>();
const T compare = data[0];
for (size_t i = 1; i < size; i++)
{
if (data[i] != compare)
{
data_is_constant = false;
break;
}
}
}
return data_is_constant;
}
bool op::Constant::are_all_data_elements_bitwise_identical() const
{
bool rc = false;
#if defined(__GNUC__) && !(__GNUC__ == 4 && __GNUC_MINOR__ == 8)
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
#pragma GCC diagnostic error "-Wswitch-enum"
#endif
switch (get_element_type())
{
case element::Type_t::boolean:
case element::Type_t::i8:
case element::Type_t::u8:
{
rc = test_bitwise_identical<uint8_t>(this);
break;
}
case element::Type_t::bf16:
case element::Type_t::f16:
case element::Type_t::i16:
case element::Type_t::u16:
{
rc = test_bitwise_identical<uint16_t>(this);
break;
}
case element::Type_t::f32:
case element::Type_t::i32:
case element::Type_t::u32:
{
rc = test_bitwise_identical<uint32_t>(this);
break;
}
case element::Type_t::f64:
case element::Type_t::i64:
case element::Type_t::u64:
{
rc = test_bitwise_identical<uint64_t>(this);
break;
}
case element::Type_t::u1:
case element::Type_t::undefined:
case element::Type_t::dynamic: break;
}
#if defined(__GNUC__) && !(__GNUC__ == 4 && __GNUC_MINOR__ == 8)
#pragma GCC diagnostic pop
#endif
return rc;
}
bool op::v0::Constant::visit_attributes(AttributeVisitor& visitor)
{
NGRAPH_OP_SCOPE(v0_Constant_visit_attributes);
visitor.on_attribute("element_type", m_element_type);
visitor.on_attribute("shape", m_shape);
if (m_data == nullptr)
{
// Filling in a fresh constant
allocate_buffer();
}
visitor.on_attribute("value", m_data);
return true;
}
bool op::v0::Constant::evaluate(const HostTensorVector& outputs,
const HostTensorVector& inputs) const
{
NGRAPH_OP_SCOPE(v0_Constant_evaluate);
auto output = outputs[0];
output->write(get_data_ptr(), output->get_size_in_bytes());
return true;
}
bool op::v0::Constant::evaluate_lower(const HostTensorVector& outputs) const
{
return evaluate(outputs, {});
}
bool op::v0::Constant::evaluate_upper(const HostTensorVector& outputs) const
{
return evaluate(outputs, {});
}
//
// We have to open up namespace blocks here to work around a problem with gcc:
//
// https://stackoverflow.com/questions/25594644/warning-specialization-of-template-in-different-namespace
//
namespace ngraph
{
namespace op
{
namespace v0
{
template <>
void Constant::write_to_buffer<string>(const element::Type& /* target_type */,
const Shape& /* target_shape */,
const vector<string>& /* source */,
void* /* target */,
size_t /* target_element_count */)
{
}
}
}
}