StridedSlice : slice and reverse new ref implementations (#3188)

* Create new iterators which allow to iterate over coordinates.

Use new iterators to speedup StridedSlice reference implementation.

* Call memcpy if reverse ref impl has nothing to reverse.

* Add unit tests for coordinate range.

* Change coordinates::RangeIterator to template.

* Yet another slice and reverse implementation.

Remove all stuff connected with ranges.

* Apply review suggestions.

* Back to ranges which base on CoordinateTransform.

* try to fix x84_32 build

* try to fix x84_32 build

* Ranges which return start, no, stride, direction.

* add input validation to coordinate_index

enable coordinate_range validation tests

* add some doxygens

* fix range increament

* add empyt range

* move SliceRange::get_value to cpp file

Co-authored-by: Patryk Elszkowski <patryk.elszkowki@intel.com>
Co-authored-by: ggalieroc <gabriele.galiero.casay@intel.com>
This commit is contained in:
Patryk Elszkowski 2020-12-10 16:36:12 +01:00 committed by GitHub
parent 6c0201108f
commit d8f00acd39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1111 additions and 63 deletions

View File

@ -0,0 +1,29 @@
//*****************************************************************************
// Copyright 2020 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.
//*****************************************************************************
#pragma once
#include <cstddef>
namespace ngraph
{
class Coordinate;
class Shape;
} // namespace ngraph
namespace ngraph
{
std::size_t coordinate_index(const Coordinate& c, const Shape& s);
}

View File

@ -0,0 +1,221 @@
//*****************************************************************************
// Copyright 2020 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.
//*****************************************************************************
#pragma once
#include <algorithm>
#include <iterator>
#include "ngraph/coordinate.hpp"
#include "ngraph/shape.hpp"
#include "ngraph/strides.hpp"
namespace ngraph
{
namespace coordinates
{
namespace impl
{
namespace
{
template <typename C>
bool has_zeros(const C& c)
{
const auto is_zero = [](size_t x) { return x == 0; };
return std::any_of(c.begin(), c.end(), is_zero);
}
} // namespace
/// \brief Class which allow to iterate over a different ranges part by part
///
template <typename Range>
class RangeIterator
{
public:
using value_type = typename Range::value_type;
using reference = typename Range::value_type;
using iterator_category = std::input_iterator_tag;
using difference_type = void;
RangeIterator(Range* r)
: m_r{r}
{
if (m_r && !m_r->is_valid())
{
m_r = nullptr;
}
}
value_type operator*() const { return m_r->get_value(); }
RangeIterator& operator++()
{
if (m_r && !m_r->increment())
{
m_r = nullptr;
}
return *this;
}
RangeIterator operator++(int) = delete;
friend bool operator==(const RangeIterator& lhs, const RangeIterator& rhs)
{
return lhs.m_r == rhs.m_r;
}
friend bool operator!=(const RangeIterator& lhs, const RangeIterator& rhs)
{
return !(lhs == rhs);
}
private:
Range* m_r;
};
/// \brief Describe slice range
///
struct CoordinateBounds
{
CoordinateBounds(const Coordinate& lower, const Coordinate& upper)
: m_lower{lower}
, m_upper{upper}
{
if (m_lower.size() != m_upper.size())
{
throw std::domain_error{"different Coordinates bonds sizes"};
}
}
Coordinate m_lower;
Coordinate m_upper;
size_t last_dim_size() const noexcept { return m_upper.back() - m_lower.back(); }
};
/// \brief helper for iterator creation which allow to stay DRY
///
template <typename Range>
struct RangeBase
{
using Iterator = RangeIterator<Range>;
Iterator begin() { return Iterator(static_cast<Range*>(this)); }
Iterator end() { return Iterator(nullptr); }
friend Iterator begin(Range& r) { return r.begin(); }
friend Iterator end(Range& r) { return r.end(); }
};
/// \brief Information how index in _Range_ should be change
///
enum class Direction
{
forward,
reverse,
};
/// \brief Range contain information which part of memory should be copied
///
struct Range
{
const size_t begin_index;
const size_t element_number;
const size_t step;
const Direction direction;
static constexpr Range make_empyt() { return Range{0, 0, 1, Direction::forward}; }
};
/// \brief Class allows to iterate over sliced Tensor part by part.
///
/// To create SliceRange use _slice_ function.
class SliceRange : public RangeBase<SliceRange>
{
public:
using value_type = Range;
SliceRange(const Shape& source_shape,
const Coordinate& source_start_corner,
const Coordinate& source_end_corner,
const Strides& strides);
value_type get_value() const;
bool increment();
bool is_valid() const noexcept { return !has_zeros(m_source_shape); }
private:
const Shape m_source_shape;
const CoordinateBounds m_bounds;
const Strides m_source_strides;
const std::vector<size_t> m_memory_strides;
Coordinate m_coordinate;
size_t m_index{0};
};
/// \brief Create SliceRange which might be used in range-base for loop
///
inline SliceRange slice(const Shape& source_shape,
const Coordinate& source_start_corner,
const Coordinate& source_end_corner,
const Strides& strides)
{
return SliceRange{source_shape, source_start_corner, source_end_corner, strides};
}
/// \brief Create SliceRange which might be used in range-base for loop
///
inline SliceRange slice(const Shape& source_shape,
const Coordinate& source_start_corner,
const Coordinate& source_end_corner)
{
return slice(source_shape,
source_start_corner,
source_end_corner,
Strides(source_shape.size(), 1));
}
/// \brief Class allows to iterate over Tensor with reverted axies part by part.
///
/// To create ReverseRange use _reverse_ function.
///
class ReverseRange : public RangeBase<ReverseRange>
{
public:
using value_type = Range;
ReverseRange(const Shape& source_shape, const AxisSet& reversed_axis);
value_type get_value() const;
bool increment();
bool is_valid() const noexcept { return !has_zeros(m_source_shape); }
private:
const Shape m_source_shape;
const std::vector<size_t> m_memory_strides;
const std::vector<Direction> m_axis_directions;
Coordinate m_coordinate;
size_t m_index{0};
};
inline ReverseRange reverse(const Shape& source_shape, const AxisSet& reversed_axis)
{
return ReverseRange(source_shape, reversed_axis);
}
} // namespace impl
using impl::Direction;
using impl::reverse;
using impl::slice;
} // namespace coordinates
} // namespace ngraph

View File

@ -31,11 +31,18 @@ namespace ngraph
/// {1,0}, {1,1}, {2,2}
class CoordinateIterator
{
public:
/// \brief Coordinates iterator constructor
/// \param target_shape The target shape for coordinates iteration
/// \param is_end The flag indicates that the coordinate iterator is the last.
CoordinateIterator(const Shape& target_shape, bool is_end = false);
CoordinateIterator(const Shape& target_shape, bool is_end);
public:
/// \brief Coordinates iterator constructor
/// \param target_shape The target shape for coordinates iteration
CoordinateIterator(const Shape& target_shape)
: CoordinateIterator(target_shape, false)
{
}
/// \brief The postfix operation increment the iterator by one.
void operator++();
@ -176,4 +183,4 @@ namespace ngraph
Shape m_target_shape;
size_t m_n_axes;
};
}
} // namespace ngraph

View File

@ -0,0 +1,45 @@
//*****************************************************************************
// Copyright 2020 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 "ngraph/coordinate_index.hpp"
#include "ngraph/coordinate.hpp"
#include "ngraph/shape.hpp"
namespace ngraph
{
std::size_t coordinate_index(const Coordinate& c, const Shape& s)
{
if (c.size() < s.size())
{
throw std::domain_error("Coordinate rank is less than shape rank.");
}
std::size_t index = 0;
std::size_t stride = 1;
std::size_t const padding = c.size() - s.size();
for (std::size_t axis = s.size(); axis-- > 0;)
{
if (s[axis] > 1)
{
index += c[axis + padding] * stride;
stride *= s[axis];
}
}
return index;
}
} // namespace ngraph

View File

@ -0,0 +1,234 @@
//*****************************************************************************
// Copyright 2020 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 "ngraph/coordinate_range.hpp"
#include <cassert>
#include <numeric>
#include <stdexcept>
#include "ngraph/coordinate_index.hpp"
namespace ngraph
{
namespace coordinates
{
namespace impl
{
namespace
{
std::vector<size_t> memory_strides(const Shape& shape)
{
std::vector<size_t> mem_strides(shape.size(), 1);
if (shape.size() > 1)
{
for (auto i = shape.size() - 1; i-- > 0;)
{
mem_strides[i] = mem_strides[i + 1] * shape[i + 1];
}
}
return mem_strides;
}
} // namespace
SliceRange::SliceRange(const Shape& source_shape,
const Coordinate& source_start_corner,
const Coordinate& source_end_corner,
const Strides& source_strides)
: m_source_shape{source_shape}
, m_bounds{source_start_corner, source_end_corner}
, m_source_strides{source_strides}
, m_memory_strides(memory_strides(source_shape))
, m_coordinate{source_start_corner}
, m_index(coordinate_index(source_start_corner, source_shape))
{
const auto axis = m_source_shape.size();
if (axis != m_bounds.m_lower.size())
{
throw std::domain_error(
"Source start corner does not have the same number of axis as the "
"source "
"space "
"shape");
}
if (axis != m_bounds.m_upper.size())
{
throw std::domain_error(
"Source end corner does not have the same number of axis as the source "
"space "
"shape");
}
if (axis != m_source_strides.size())
{
throw std::domain_error(
"Source strides do not have the same number of axis as the source "
"space "
"shape");
}
if (axis != m_memory_strides.size())
{
throw std::runtime_error("Something goes wrong");
}
}
SliceRange::value_type SliceRange::get_value() const
{
if (m_source_shape.empty())
{
return Range::make_empyt();
}
const size_t element_no = (m_bounds.last_dim_size() + m_source_strides.back() - 1) /
m_source_strides.back();
return Range{m_index, element_no, m_source_strides.back(), Direction::forward};
}
bool SliceRange::increment()
{
// during increment rage omit last dim so at least two dims are required to proceed
if (m_coordinate.size() < 2)
{
return false;
}
// omit last dim - it will be return in slice_range
for (auto axis = m_coordinate.size() - 1; axis-- > 0;)
{
const auto index_step = m_source_strides[axis] * m_memory_strides[axis];
m_coordinate[axis] += m_source_strides[axis];
m_index += index_step;
if (m_coordinate[axis] < m_bounds.m_upper[axis])
{
assert(m_index < shape_size(m_source_shape));
return true;
}
const auto difference = m_coordinate[axis] - m_bounds.m_lower[axis];
m_coordinate[axis] = m_bounds.m_lower[axis];
// back on beginning of axis memory
m_index -= difference * m_memory_strides[axis];
}
return false;
}
namespace
{
std::vector<Direction> axis_direcions(size_t size, const AxisSet& reversed_axis)
{
const auto max_reversed_axis = [&] {
return *std::max_element(reversed_axis.begin(), reversed_axis.end());
};
if (!reversed_axis.empty() && !(max_reversed_axis() < size))
{
throw std::domain_error(
"Reversed axis have axes above the source space shape");
}
std::vector<Direction> directions(size, Direction::forward);
for (auto i : reversed_axis)
{
directions[i] = Direction::reverse;
}
return directions;
}
Coordinate start_coordinate(const Shape& s, const std::vector<Direction>& direction)
{
Coordinate coordiante(s.size(), 0);
for (size_t i = 0; i < s.size(); ++i)
{
if (direction[i] == Direction::reverse)
{
coordiante[i] = s[i] - 1;
}
}
return coordiante;
}
} // namespace
ReverseRange::ReverseRange(const Shape& source_shape, const AxisSet& reversed_axis)
: m_source_shape{source_shape}
, m_memory_strides(memory_strides(source_shape))
, m_axis_directions(axis_direcions(source_shape.size(), reversed_axis))
, m_coordinate(source_shape.size(), 0)
, m_index(coordinate_index(start_coordinate(source_shape, m_axis_directions),
source_shape))
{
}
ReverseRange::value_type ReverseRange::get_value() const
{
if (m_source_shape.empty())
{
return Range::make_empyt();
}
assert(m_memory_strides.back() == 1);
return Range{m_index,
m_source_shape.back(),
m_memory_strides.back(),
m_axis_directions.back()};
}
bool ReverseRange::increment()
{
// during increment rage omit last dim so at least two dims are required to proceed
if (m_coordinate.size() < 2)
{
return false;
}
// omit last dim - it will be return in reverse_range
for (auto axis = m_coordinate.size() - 1; axis-- > 0;)
{
const auto index_step = m_memory_strides[axis];
++m_coordinate[axis];
if (m_axis_directions[axis] == Direction::forward)
{
m_index += index_step;
}
else
{
m_index -= index_step;
}
if (m_coordinate[axis] < m_source_shape[axis])
{
assert(0 <= m_index && m_index < shape_size(m_source_shape));
return true;
}
m_coordinate[axis] = 0;
// back on beginning of axis memory
if (m_axis_directions[axis] == Direction::forward)
{
m_index -= m_source_shape[axis] * m_memory_strides[axis];
}
else
{
m_index += m_source_shape[axis] * m_memory_strides[axis];
}
}
return false;
}
} // namespace impl
} // namespace coordinates
} // namespace ngraph

View File

@ -14,6 +14,8 @@
// limitations under the License.
//*****************************************************************************
#include "ngraph/coordinate_transform.hpp"
#include <algorithm>
#include <cstdio>
#include <iostream>
@ -23,7 +25,7 @@
#include "ngraph/axis_vector.hpp"
#include "ngraph/coordinate_diff.hpp"
#include "ngraph/coordinate_transform.hpp"
#include "ngraph/coordinate_index.hpp"
#include "ngraph/except.hpp"
#include "ngraph/shape.hpp"
#include "ngraph/strides.hpp"
@ -44,7 +46,7 @@ namespace
Coordinate default_source_start_corner(size_t n_axes) { return Coordinate(n_axes, 0); }
Coordinate default_source_end_corner(const Shape& source_shape) { return source_shape; }
}
} // namespace
CoordinateTransformBasic::CoordinateTransformBasic(const Shape& source_shape)
: m_source_shape(source_shape)
@ -54,20 +56,7 @@ CoordinateTransformBasic::CoordinateTransformBasic(const Shape& source_shape)
// Compute the index of a source-space coordinate in the buffer.
size_t CoordinateTransformBasic::index(const Coordinate& c) const noexcept
{
size_t index = 0;
size_t stride = 1;
size_t const padding = c.size() - m_source_shape.size();
for (size_t axis = m_source_shape.size(); axis-- > 0;)
{
if (m_source_shape[axis] > 1)
{
index += c[axis + padding] * stride;
stride *= m_source_shape[axis];
}
}
return index;
return coordinate_index(c, m_source_shape);
}
CoordinateIterator CoordinateTransformBasic::begin() const noexcept

View File

@ -15,39 +15,62 @@
//*****************************************************************************
#include <cmath>
#include <stdio.h>
#include <cstring>
#include <iterator>
#include "ngraph/check.hpp"
#include "ngraph/coordinate_range.hpp"
#include "ngraph/runtime/reference/reverse.hpp"
using namespace ngraph;
void runtime::reference::reverse(const char* arg,
char* out,
const Shape& arg_shape,
const Shape& out_shape,
const AxisSet& reversed_axes,
size_t elem_size)
namespace ngraph
{
// In fact arg_shape == out_shape, but we'll use both for stylistic consistency with
// other kernels.
CoordinateTransform arg_transform(arg_shape);
CoordinateTransform output_transform(out_shape);
for (Coordinate out_coord : output_transform)
namespace runtime
{
Coordinate arg_coord = out_coord;
for (size_t i = 0; i < arg_coord.size(); i++)
namespace reference
{
if (reversed_axes.count(i) != 0)
void reverse(const char* arg,
char* out,
const Shape& arg_shape,
const Shape& out_shape,
const AxisSet& reversed_axes,
size_t elem_size)
{
arg_coord[i] = arg_shape[i] - arg_coord[i] - 1;
}
}
NGRAPH_CHECK(shape_size(arg_shape) == shape_size(out_shape));
memcpy(out + output_transform.index(out_coord) * elem_size,
arg + arg_transform.index(arg_coord) * elem_size,
elem_size);
}
}
const bool nothing_to_revers = reversed_axes.empty();
if (nothing_to_revers)
{
std::memcpy(out, arg, shape_size(arg_shape) * elem_size);
return;
}
auto dst_mem = out;
for (auto range : coordinates::reverse(arg_shape, reversed_axes))
{
auto src_index = range.begin_index;
if (range.direction == coordinates::Direction::forward)
{
for (size_t i = 0; i < range.element_number; src_index += range.step, ++i)
{
const auto src_mem = arg + src_index * elem_size;
std::memcpy(dst_mem, src_mem, elem_size);
std::advance(dst_mem, elem_size);
}
}
else
{
for (size_t i = 0; i < range.element_number; src_index -= range.step, ++i)
{
const auto src_mem = arg + src_index * elem_size;
std::memcpy(dst_mem, src_mem, elem_size);
std::advance(dst_mem, elem_size);
}
}
}
}
} // namespace reference
} // namespace runtime
} // namespace ngraph

View File

@ -14,11 +14,12 @@
// limitations under the License.
//*****************************************************************************
#include <cmath>
#include <stdio.h>
#include "ngraph/runtime/reference/slice.hpp"
#include <cstring>
#include "ngraph/check.hpp"
#include "ngraph/runtime/reference/slice.hpp"
#include "ngraph/coordinate_range.hpp"
namespace ngraph
{
@ -35,27 +36,28 @@ namespace ngraph
const Shape& out_shape,
size_t elem_size)
{
CoordinateTransform input_transform(arg_shape, lower_bounds, upper_bounds, strides);
CoordinateTransform output_transform(out_shape);
const CoordinateTransform input_transform(
arg_shape, lower_bounds, upper_bounds, strides);
CoordinateTransform::Iterator output_it = output_transform.begin();
const CoordinateTransform output_transform(out_shape);
NGRAPH_CHECK(shape_size(input_transform.get_target_shape()) ==
shape_size(output_transform.get_target_shape()));
for (const Coordinate& in_coord : input_transform)
auto dst_mem = out;
for (auto range :
coordinates::slice(arg_shape, lower_bounds, upper_bounds, strides))
{
if (output_it == output_transform.end())
break;
const Coordinate& out_coord = *output_it;
memcpy(out + output_transform.index(out_coord) * elem_size,
arg + input_transform.index(in_coord) * elem_size,
elem_size);
++output_it;
auto src_index = range.begin_index;
for (size_t i = 0; i < range.element_number; src_index += range.step, ++i)
{
const auto src_mem = arg + src_index * elem_size;
std::memcpy(dst_mem, src_mem, elem_size);
std::advance(dst_mem, elem_size);
}
}
}
}
}
}
} // namespace reference
} // namespace runtime
} // namespace ngraph

View File

@ -54,6 +54,7 @@ set(SRC
control_dependencies.cpp
convert_u1_to_string.cpp
coordinate.cpp
coordinate_range.cpp
copy.cpp
element_type.cpp
eval.cpp

View File

@ -29,6 +29,30 @@ using namespace ngraph;
static string s_manifest = "${MANIFEST}";
NGRAPH_TEST(${BACKEND_NAME}, nothing_to_reverse)
{
Shape shape{8};
auto A = make_shared<op::Parameter>(element::f32, shape);
auto f = make_shared<Function>(
make_shared<op::v1::Reverse>(A,
op::Constant::create(element::i64, {0}, std::vector<int>{}),
op::v1::Reverse::Mode::INDEX),
ParameterVector{A});
auto backend = runtime::Backend::create("${BACKEND_NAME}");
// Create some tensors for input/output
auto a = backend->create_tensor(element::f32, shape);
copy_data(a, vector<float>{0, 1, 2, 3, 4, 5, 6, 7});
auto result = backend->create_tensor(element::f32, shape);
auto handle = backend->compile(f);
handle->call_with_validate({result}, {a});
EXPECT_TRUE(test::all_close_f((vector<float>{0, 1, 2, 3, 4, 5, 6, 7}),
read_vector<float>(result),
MIN_FLOAT_TOLERANCE_BITS));
}
NGRAPH_TEST(${BACKEND_NAME}, reverse_1d)
{
Shape shape{8};

View File

@ -0,0 +1,470 @@
//*****************************************************************************
// Copyright 2020 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 <algorithm>
#include <numeric>
#include <utility>
#include "gtest/gtest.h"
#include <ngraph/coordinate_range.hpp>
using namespace ngraph;
using namespace ngraph::coordinates;
using Index = size_t;
using ExpectedOutput = std::vector<std::pair<Index, Coordinate>>;
///
///
/// SliceRange
///
///
TEST(coordinate_range, slice_range_shape0d)
{
const Shape s;
const Coordinate start_corner(s.size());
auto slice_range = slice(s, start_corner, s);
auto it = slice_range.begin();
EXPECT_EQ(it, begin(slice_range));
EXPECT_FALSE(it == slice_range.end());
auto v = *it; // if it is not end it has to be dereferencable;
(void)v;
EXPECT_TRUE(++it == slice_range.end());
}
TEST(coordinate_range, slice_range_shape1d)
{
const Shape s{3};
const Coordinate start_corner(s.size());
const ExpectedOutput expected{{0, {0}}, {1, {1}}, {2, {2}}};
ASSERT_EQ(expected.size(), shape_size(s)) << "check epxected data";
auto expected_val = begin(expected);
for (auto slice_range : slice(s, start_corner, s))
{
auto index = slice_range.begin_index;
for (size_t i = 0; i < slice_range.element_number; index += slice_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}
TEST(coordinate_range, slice_range_shape2d)
{
const Shape s{2, 3};
const Coordinate start_corner(s.size());
// clang-format off
const ExpectedOutput expected{
{0, {0, 0}}, {1, {0, 1}}, {2, {0, 2}},
{3, {1, 0}}, {4, {1, 1}}, {5, {1, 2}}};
// clang-format on
ASSERT_EQ(expected.size(), shape_size(s)) << "check epxected data";
auto expected_val = begin(expected);
for (auto slice_range : slice(s, start_corner, s))
{
auto index = slice_range.begin_index;
for (size_t i = 0; i < slice_range.element_number; index += slice_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}
TEST(coordinate_range, slice_range_shape3d)
{
const Shape s{2, 3, 4};
const Coordinate start_corner(s.size());
// clang-format off
const ExpectedOutput expected{
{0, {0, 0, 0}}, {1, {0, 0, 1}}, {2, {0, 0, 2}}, {3, {0, 0, 3}},
{4, {0, 1, 0}}, {5, {0, 1, 1}}, {6, {0, 1, 2}}, {7, {0, 1, 3}},
{8, {0, 2, 0}}, {9, {0, 2, 1}}, {10, {0, 2, 2}}, {11, {0, 2, 3}},
{12, {1, 0, 0}}, {13, {1, 0, 1}}, {14, {1, 0, 2}}, {15, {1, 0, 3}},
{16, {1, 1, 0}}, {17, {1, 1, 1}}, {18, {1, 1, 2}}, {19, {1, 1, 3}},
{20, {1, 2, 0}}, {21, {1, 2, 1}}, {22, {1, 2, 2}}, {23, {1, 2, 3}}};
// clang-format on
ASSERT_EQ(expected.size(), shape_size(s)) << "check epxected data";
auto expected_val = begin(expected);
for (auto slice_range : slice(s, start_corner, s))
{
auto index = slice_range.begin_index;
for (size_t i = 0; i < slice_range.element_number; index += slice_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected));
}
TEST(coordinate_range, slice_range_zero_sized_axis)
{
const Shape s{2, 0, 4};
const Coordinate start_corner(s.size());
auto slice_range = slice(s, start_corner, s);
auto it = slice_range.begin();
EXPECT_TRUE(it == slice_range.end()) << "Expect empyt range";
}
///
/// slice specyfic test
///
TEST(coordinate_range, slice_range_input_validataion)
{
const Shape s{10, 10, 10};
EXPECT_THROW(slice(s, {1}, {1}), std::domain_error);
EXPECT_THROW(slice(s, s, {1}), std::domain_error);
EXPECT_THROW(slice(s, {1}, s), std::domain_error);
EXPECT_THROW(slice(s, s, s, {}), std::domain_error);
}
namespace
{
Shape sliced_shape(const std::vector<size_t>& start_corner,
const std::vector<size_t>& end_corner)
{
Shape s;
std::transform(end_corner.begin(),
end_corner.end(),
start_corner.begin(),
std::back_inserter(s),
[](size_t e, size_t b) { return e - b; });
return s;
}
Shape sliced_shape(const std::vector<size_t>& start_corner,
const std::vector<size_t>& end_corner,
const std::vector<size_t>& strides)
{
Shape s = sliced_shape(start_corner, end_corner);
std::transform(s.begin(), s.end(), strides.begin(), s.begin(), [](size_t e, size_t s) {
return (e + s - 1) / s;
});
return s;
}
} // namespace
TEST(coordinate_range, slice_range_corner)
{
const Shape s{10, 10};
const Coordinate source_start_corner{3, 3};
const Coordinate source_end_corner{6, 6};
const ExpectedOutput expected{{33, {3, 3}},
{34, {3, 4}},
{35, {3, 5}},
{43, {4, 3}},
{44, {4, 4}},
{45, {4, 5}},
{53, {5, 3}},
{54, {5, 4}},
{55, {5, 5}}};
ASSERT_EQ(expected.size(), shape_size(sliced_shape(source_start_corner, source_end_corner)))
<< "check epxected data";
auto expected_val = begin(expected);
for (auto slice_range : slice(s, source_start_corner, source_end_corner))
{
auto index = slice_range.begin_index;
for (size_t i = 0; i < slice_range.element_number; index += slice_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}
TEST(coordinate_range, slice_range_strides)
{
const Shape s{10, 10};
const Coordinate source_start_corner{0, 0};
const Coordinate source_end_corner{s};
const Strides source_strides = Strides({2, 3});
// clang-format off
const ExpectedOutput expected{
{0, {0, 0}}, {3, {0, 3}}, {6, {0, 6}}, {9, {0, 9}},
{20, {2, 0}}, {23, {2, 3}}, {26, {2, 6}}, {29, {2, 9}},
{40, {4, 0}}, {43, {4, 3}}, {46, {4, 6}}, {49, {4, 9}},
{60, {6, 0}}, {63, {6, 3}}, {66, {6, 6}}, {69, {6, 9}},
{80, {8, 0}}, {83, {8, 3}}, {86, {8, 6}}, {89, {8, 9}}};
// clang-format on
ASSERT_EQ(expected.size(),
shape_size(sliced_shape(source_start_corner, source_end_corner, source_strides)))
<< "check epxected data";
auto expected_val = begin(expected);
for (auto slice_range : slice(s, source_start_corner, source_end_corner, source_strides))
{
auto index = slice_range.begin_index;
for (size_t i = 0; i < slice_range.element_number; index += slice_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}
///
///
/// ReverseRange
///
///
TEST(coordinate_range, reverse_range_shape0d)
{
const Shape s;
const AxisSet reverset_axis{};
auto reverse_range = reverse(s, reverset_axis);
auto it = reverse_range.begin();
EXPECT_EQ(it, begin(reverse_range));
auto v = *it; // if it is not end it has to be dereferencable;
(void)v;
EXPECT_TRUE(++it == reverse_range.end());
}
TEST(coordinate_range, reverse_range_shape1d)
{
const Shape s{3};
const AxisSet reverset_axis{};
const ExpectedOutput expected{{0, {0}}, {1, {1}}, {2, {2}}};
EXPECT_EQ(expected.size(), shape_size(s)) << "check epxected data";
auto expected_val = begin(expected);
for (auto reverse_range : reverse(s, reverset_axis))
{
auto index = reverse_range.begin_index;
ASSERT_EQ(reverse_range.direction, Direction::forward);
for (size_t i = 0; i < reverse_range.element_number; index += reverse_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}
TEST(coordinate_range, reverse_range_shape2d)
{
const Shape s{2, 3};
const AxisSet reverset_axis{};
// clang-format off
const ExpectedOutput expected{
{0, {0, 0}}, {1, {0, 1}}, {2, {0, 2}},
{3, {1, 0}}, {4, {1, 1}}, {5, {1, 2}}};
// clang-format on
EXPECT_EQ(expected.size(), shape_size(s)) << "check epxected data";
auto expected_val = begin(expected);
for (auto reverse_range : reverse(s, reverset_axis))
{
auto index = reverse_range.begin_index;
ASSERT_EQ(reverse_range.direction, Direction::forward);
for (size_t i = 0; i < reverse_range.element_number; index += reverse_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}
TEST(coordinate_range, reverse_range_shape3d)
{
const Shape s{2, 3, 4};
const AxisSet reverset_axis{};
// clang-format off
const ExpectedOutput expected{
{0, {0, 0, 0}}, {1, {0, 0, 1}}, {2, {0, 0, 2}}, {3, {0, 0, 3}},
{4, {0, 1, 0}}, {5, {0, 1, 1}}, {6, {0, 1, 2}}, {7, {0, 1, 3}},
{8, {0, 2, 0}}, {9, {0, 2, 1}}, {10, {0, 2, 2}}, {11, {0, 2, 3}},
{12, {1, 0, 0}}, {13, {1, 0, 1}}, {14, {1, 0, 2}}, {15, {1, 0, 3}},
{16, {1, 1, 0}}, {17, {1, 1, 1}}, {18, {1, 1, 2}}, {19, {1, 1, 3}},
{20, {1, 2, 0}}, {21, {1, 2, 1}}, {22, {1, 2, 2}}, {23, {1, 2, 3}}};
// clang-format on
EXPECT_EQ(expected.size(), shape_size(s)) << "check epxected data";
auto expected_val = begin(expected);
for (auto reverse_range : reverse(s, reverset_axis))
{
auto index = reverse_range.begin_index;
ASSERT_EQ(reverse_range.direction, Direction::forward);
for (size_t i = 0; i < reverse_range.element_number; index += reverse_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}
TEST(coordinate_range, reverse_range_zero_sized_axis)
{
const Shape s{2, 0, 4};
auto reverse_range = reverse(s, {});
auto it = reverse_range.begin();
EXPECT_TRUE(it == reverse_range.end()) << "Expect empyt range";
}
///
/// reverse specyfic test
///
TEST(coordinate_range, reverse_range_input_validataion)
{
const Shape s{10, 10, 10};
EXPECT_THROW(reverse(s, {10}), std::domain_error);
}
TEST(coordinate_range, reverse_range_2d)
{
const Shape s{3, 10};
const AxisSet reverset_axis{1};
// clang-format off
const ExpectedOutput expected{
{9, {0, 9}}, {8, {0, 8}}, {7, {0, 7}}, {6, {0, 6}}, {5, {0, 5}}, {4, {0, 4}}, {3, {0, 3}}, {2, {0, 2}}, {1, {0, 1}}, {0, {0, 0}},
{19, {1, 9}}, {18, {1, 8}}, {17, {1, 7}}, {16, {1, 6}}, {15, {1, 5}}, {14, {1, 4}}, {13, {1, 3}}, {12, {1, 2}}, {11, {1, 1}}, {10, {1, 0}},
{29, {2, 9}}, {28, {2, 8}}, {27, {2, 7}}, {26, {2, 6}}, {25, {2, 5}}, {24, {2, 4}}, {23, {2, 3}}, {22, {2, 2}}, {21, {2, 1}}, {20, {2, 0}}};
// clang-format on
auto expected_val = begin(expected);
for (auto reverse_range : reverse(s, reverset_axis))
{
auto index = reverse_range.begin_index;
ASSERT_EQ(reverse_range.direction, Direction::reverse);
for (size_t i = 0; i < reverse_range.element_number; index -= reverse_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}
TEST(coordinate_range, reverse_1_range_3d)
{
const Shape s{3, 3, 3};
const AxisSet reverset_axis{1};
// clang-format off
const ExpectedOutput expected{
{6, {0, 2, 0}}, {7, {0, 2, 1}}, {8, {0, 2, 2}},
{3, {0, 1, 0}}, {4, {0, 1, 1}}, {5, {0, 1, 2}},
{0, {0, 0, 0}}, {1, {0, 0, 1}}, {2, {0, 0, 2}},
{15, {1, 2, 0}}, {16, {1, 2, 1}}, {17, {1, 2, 2}},
{12, {1, 1, 0}}, {13, {1, 1, 1}}, {14, {1, 1, 2}},
{9, {1, 0, 0}}, {10, {1, 0, 1}}, {11, {1, 0, 2}},
{24, {2, 2, 0}}, {25, {2, 2, 1}}, {26, {2, 2, 2}},
{21, {2, 1, 0}}, {22, {2, 1, 1}}, {23, {2, 1, 2}},
{18, {2, 0, 0}}, {19, {2, 0, 1}}, {20, {2, 0, 2}}};
// clang-format on
auto expected_val = begin(expected);
for (auto reverse_range : reverse(s, reverset_axis))
{
auto index = reverse_range.begin_index;
ASSERT_EQ(reverse_range.direction, Direction::forward);
for (size_t i = 0; i < reverse_range.element_number; index += reverse_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}
TEST(coordinate_range, reverse_2_range_3d)
{
const Shape s{3, 3, 3};
const AxisSet reverset_axis{1, 2};
// clang-format off
const ExpectedOutput expected{
{8, {0, 2, 2}}, {7, {0, 2, 1}}, {6, {0, 2, 0}},
{5, {0, 1, 2}}, {4, {0, 1, 1}}, {3, {0, 1, 0}},
{2, {0, 0, 2}}, {1, {0, 0, 1}}, {0, {0, 0, 0}},
{17, {1, 2, 2}}, {16, {1, 2, 1}}, {15, {1, 2, 0}},
{14, {1, 1, 2}}, {13, {1, 1, 1}}, {12, {1, 1, 0}},
{11, {1, 0, 2}}, {10, {1, 0, 1}}, {9, {1, 0, 0}},
{26, {2, 2, 2}}, {25, {2, 2, 1}}, {24, {2, 2, 0}},
{23, {2, 1, 2}}, {22, {2, 1, 1}}, {21, {2, 1, 0}},
{20, {2, 0, 2}}, {19, {2, 0, 1}}, {18, {2, 0, 0}}};
// clang-format on
auto expected_val = begin(expected);
for (auto reverse_range : reverse(s, reverset_axis))
{
auto index = reverse_range.begin_index;
ASSERT_EQ(reverse_range.direction, Direction::reverse);
for (size_t i = 0; i < reverse_range.element_number; index -= reverse_range.step, ++i)
{
EXPECT_EQ(index, expected_val->first);
++expected_val;
}
}
EXPECT_TRUE(expected_val == end(expected)) << "not all expected values return, ("
<< std::distance(expected_val, end(expected))
<< " is missing)";
}

View File

@ -245,6 +245,9 @@ IE_GPU.onnx_model_rnn_fwd_mixed_seq_len_const
IE_GPU.onnx_model_gru_fwd_mixed_seq_len
IE_GPU.onnx_model_gru_fwd_mixed_seq_len_const
## Const layer has incorrect dimensions in the output data
IE_CPU.nothing_to_reverse
#-------------------------------------------------------------------------------
#