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:
parent
6c0201108f
commit
d8f00acd39
29
ngraph/core/reference/include/ngraph/coordinate_index.hpp
Normal file
29
ngraph/core/reference/include/ngraph/coordinate_index.hpp
Normal 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);
|
||||
}
|
221
ngraph/core/reference/include/ngraph/coordinate_range.hpp
Normal file
221
ngraph/core/reference/include/ngraph/coordinate_range.hpp
Normal 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
|
@ -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
|
||||
|
45
ngraph/core/reference/src/coordinate_index.cpp
Normal file
45
ngraph/core/reference/src/coordinate_index.cpp
Normal 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
|
234
ngraph/core/reference/src/coordinate_range.cpp
Normal file
234
ngraph/core/reference/src/coordinate_range.cpp
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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};
|
||||
|
470
ngraph/test/coordinate_range.cpp
Normal file
470
ngraph/test/coordinate_range.cpp
Normal 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)";
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user