Commit 80022ea1 authored by Ivan Tikhonov's avatar Ivan Tikhonov Committed by Scott Cyphers

New DynSlice (Strided Slice) realization (#3662)

* Strided slice

* Strided slice

* default value for strides

* Added new strided slice test, enabled old tests, refactoring

* Refactoring

* Autogenerated file: dyn_replace_slice tests

* Renaming

* Fix codestyle

* Fix build on MacOS

* Fix codestyle

* Add several tests in unit_test.manifest to skip it on PlaidML

* Disable all dyn_replace_slice tests on PlaidML
parent 04f212b7
......@@ -281,3 +281,6 @@ random_uniform_all_static_seed_used
random_uniform_seed_use_dynamic
random_uniform_all_static_range_dynamic
random_uniform_dynamic_shapes
# shapes with zeros dimensions like (5, 0, 5) not supported in PlaidML backend
dyn_replace_slice
\ No newline at end of file
......@@ -618,31 +618,31 @@ void ngraph::infer_auto_padding(const Shape& image_shape,
PartialShape ngraph::infer_slice_shape(const Node* node,
const PartialShape& input_shape,
const std::vector<int64_t>& lb,
const std::vector<int64_t>& ub,
const std::vector<int64_t>& str,
const AxisSet& lb_mask,
const AxisSet& ub_mask,
const AxisSet& new_axis,
const AxisSet& shrink_axis,
const std::vector<int64_t>& begin,
const std::vector<int64_t>& end,
const std::vector<int64_t>& strides,
const AxisSet& begin_mask,
const AxisSet& end_mask,
const AxisSet& new_axis_mask,
const AxisSet& shrink_axis_mask,
const AxisSet& ellipsis_mask)
{
if (lb.size() && ub.size())
if (begin.size() && end.size())
{
NODE_VALIDATION_CHECK(node,
lb.size() == ub.size(),
begin.size() == end.size(),
"Lower bounds and Upper bounds needs to have same number of values");
}
if (lb.size() && str.size())
if (begin.size() && strides.size())
{
NODE_VALIDATION_CHECK(node,
lb.size() == str.size(),
begin.size() == strides.size(),
"Lower bounds and strides needs to have same number of values");
}
if (ub.size() && str.size())
if (end.size() && strides.size())
{
NODE_VALIDATION_CHECK(node,
ub.size() == str.size(),
end.size() == strides.size(),
"Upper bounds and strides needs to have same number of values");
}
......@@ -651,221 +651,138 @@ PartialShape ngraph::infer_slice_shape(const Node* node,
return PartialShape::dynamic();
}
size_t max_dims = size_t(input_shape.rank()) + new_axis.size();
std::vector<Dimension> dim;
size_t bounds_size =
lb.size() ? lb.size() : (ub.size() ? ub.size() : (str.size() ? str.size() : 0));
size_t ellipsis_pos1 = ellipsis_mask.size() ? *ellipsis_mask.begin() : max_dims;
size_t ellipsis_pos2 = max_dims;
bounds_size -= ellipsis_pos1;
if (bounds_size > 0 && (max_dims - bounds_size) > ellipsis_pos1)
{
ellipsis_pos2 = max_dims - bounds_size;
}
std::vector<Dimension> begin_dms(max_dims, 0);
std::vector<Dimension> end_dms(max_dims, -1);
std::vector<Dimension> stride_dms(max_dims, 1);
std::vector<Dimension> out_dims;
size_t j = 0;
size_t k = 0;
size_t bj = 0;
size_t ej = 0;
size_t sj = 0;
for (size_t i = 0; i < max_dims; i++)
size_t input_shape_idx = 0;
for (size_t axis = 0; axis < begin.size(); ++axis)
{
if (i >= ellipsis_pos1 && i < ellipsis_pos2)
// add all dimensions hidden under the ellipsis mask if ellipsis mask is set
if (ellipsis_mask.count(axis))
{
if (new_axis.find(i) == new_axis.end())
// only one bit in ellipsis mask is allowed
int num_new_axis_after_ellipses = 0;
int num_input_axis_before_ellipses = 0;
for (size_t i = 0; i < axis; ++i)
{
if (end_dms[i].is_static() && int64_t(end_dms[i]) < 0)
if (!new_axis_mask.count(i))
{
end_dms[i] = input_shape[j++] + end_dms[i];
num_input_axis_before_ellipses++;
}
}
else
{
end_dms[i] = begin_dms[i];
}
if (end_dms[i].is_dynamic() || begin_dms[i].is_dynamic() || stride_dms[i].is_dynamic())
for (size_t i = axis + 1; i < begin.size(); ++i)
{
out_dims.push_back(Dimension::dynamic());
}
else
if (new_axis_mask.count(i))
{
out_dims.push_back(static_cast<int64_t>(
ceil(static_cast<float>(abs(int64_t(end_dms[i]) - int64_t(begin_dms[i])) + 1) /
static_cast<float>(abs(int64_t(stride_dms[i]))))));
num_new_axis_after_ellipses++;
}
k = ellipsis_pos1;
continue;
}
stride_dms[i] = (str.size() > sj && str[sj] != 0) ? str[sj++] : 1;
// Use lower_bounds if mask is not set
if (lb_mask.find(j) == lb_mask.end())
int64_t num_input_axis_after_ellipses =
(begin.size() - axis - num_new_axis_after_ellipses -
1); // -1 because it's a position of ellipses
int64_t num_of_hidden_dims = input_shape.to_shape().size() -
num_input_axis_after_ellipses -
num_input_axis_before_ellipses;
for (int64_t i = 0; i < num_of_hidden_dims; ++i)
{
if (lb.size() > bj)
{
begin_dms[i] = lb[bj];
dim.emplace_back(input_shape[input_shape_idx]);
input_shape_idx++;
}
else if (stride_dms[i].is_dynamic())
{
begin_dms[i] = Dimension::dynamic();
}
else if (int64_t(stride_dms[i]) > 0)
{
begin_dms[i] = 0;
}
else
{
begin_dms[i] = -1;
}
}
else if (stride_dms[i].is_dynamic())
// add new single dimension if new_axis_mask is set
if (new_axis_mask.count(axis))
{
begin_dms[i] = Dimension::dynamic();
dim.emplace_back(1);
}
else if (int64_t(stride_dms[i]) > 0)
// skip this dimension if shrink_axis_mask is set
else if (shrink_axis_mask.count(axis))
{
begin_dms[i] = 0;
input_shape_idx++;
}
// calculating dimension (begin, end, begin_mask, end_mask, stride)
else
{
begin_dms[i] = -1;
}
bj++;
int64_t lb = begin[axis];
int64_t ub = end[axis];
if (begin_dms[i].is_static() && int64_t(begin_dms[i]) < 0)
// convert negative indexes to positive
// take max for this case: if abs(lb) > input_shape[input_shape_idx],then after
// conversion lb < 0
// so according to tensorflow and numpy we just get 0
if (lb < 0)
{
begin_dms[i] = input_shape[j] + begin_dms[i];
lb = std::max(int64_t(input_shape[input_shape_idx]) + lb, int64_t(0));
}
// Clipping 'begin'
if (begin_dms[i].is_static())
{
if (int64_t(begin_dms[i]) < 0)
{
begin_dms[i] = 0;
}
else if (input_shape[j].is_dynamic())
{
begin_dms[i] = Dimension::dynamic();
}
else if (int64_t(begin_dms[i]) >= int64_t(input_shape[j]))
if (ub < 0)
{
begin_dms[i] = input_shape[j] - 1;
}
ub = std::max(int64_t(input_shape[input_shape_idx]) + ub, int64_t(0));
}
// Use upper_bounds if mask is not set
if (ub_mask.find(j) == ub_mask.end())
{
Dimension end_dms_tmp;
// apply restrictions when begin or end values more than max possible values.
lb = std::min(int64_t(input_shape[input_shape_idx]), lb);
ub = std::min(int64_t(input_shape[input_shape_idx]), ub);
if (ub.size() <= ej)
// set default value for stride or use given value
int64_t stride = 1;
if (strides.size() > axis)
{
end_dms_tmp = end_dms[i];
}
else if (stride_dms[i].is_dynamic())
{
end_dms_tmp = Dimension::dynamic();
}
else if (int64_t(stride_dms[i]) > 0)
{
end_dms_tmp = ub[ej] - 1;
}
else
{
end_dms_tmp = ub[ej] + 1;
stride = strides[axis];
}
if (ub.size() > ej)
NODE_VALIDATION_CHECK(node, stride != 0, "Stride must be non-zero");
int64_t dimension = 0;
if (stride < 0)
{
end_dms[i] = end_dms_tmp;
}
else if (stride_dms[i].is_dynamic())
// apply masks
if (begin_mask.count(axis))
{
end_dms[i] = Dimension::dynamic();
lb = int64_t(input_shape[input_shape_idx]) - 1;
}
else if (int64_t(stride_dms[i]) > 0)
if (end_mask.count(axis))
{
end_dms[i] = -1;
ub = -1;
}
else
lb = std::min(lb, int64_t(input_shape[input_shape_idx]) - 1);
lb -= 1; // we always get 1st element, so we need decrease range
if (ub <= lb)
{
end_dms[i] = 0;
dimension = (ub - lb) / stride + 1;
}
}
else
{
if (stride_dms[i].is_dynamic())
// apply masks
if (begin_mask.count(axis))
{
end_dms[i] = Dimension::dynamic();
lb = 0;
}
else if (int64_t(stride_dms[i]) > 0)
if (end_mask.count(axis))
{
end_dms[i] = -1;
ub = int64_t(input_shape[input_shape_idx]);
}
else
{
end_dms[i] = 0;
}
}
ej++;
if (end_dms[i].is_static() && int64_t(end_dms[i]) < 0)
{
end_dms[i] = input_shape[j] + end_dms[i];
}
// Clipping 'end'
if (end_dms[i].is_static())
{
if (int64_t(end_dms[i]) < 0)
{
end_dms[i] = 0;
}
else if (input_shape[j].is_dynamic())
lb += 1; // we always get 1st element, so we need decrease range
if (ub >= lb)
{
end_dms[i] = Dimension::dynamic();
}
else if (int64_t(end_dms[i]) >= int64_t(input_shape[j]))
{
end_dms[i] = input_shape[j] - 1;
dimension = (ub - lb) / stride + 1;
}
}
if (new_axis.find(i) == new_axis.end())
{
j++;
dim.emplace_back(dimension);
input_shape_idx++;
}
else
{
end_dms[i] = 0;
}
if (shrink_axis.find(k) != shrink_axis.end())
{
end_dms[i] = begin_dms[i];
}
else if (end_dms[i].is_dynamic() || begin_dms[i].is_dynamic() || stride_dms[i].is_dynamic())
// get remaining values
for (; input_shape_idx < input_shape.to_shape().size(); ++input_shape_idx)
{
out_dims.push_back(Dimension::dynamic());
}
else
{
out_dims.push_back(static_cast<int64_t>(
ceil(static_cast<float>(abs(int64_t(end_dms[i]) - int64_t(begin_dms[i])) + 1) /
static_cast<float>(abs(int64_t(stride_dms[i]))))));
dim.emplace_back(input_shape[input_shape_idx]);
}
k++;
}
return out_dims;
return dim;
}
......@@ -94,12 +94,12 @@ namespace ngraph
PartialShape infer_slice_shape(const Node* node,
const PartialShape& input_shape,
const std::vector<int64_t>& lb,
const std::vector<int64_t>& ub,
const std::vector<int64_t>& str,
const AxisSet& lb_mask,
const AxisSet& ub_mask,
const AxisSet& new_axis,
const AxisSet& shrink_mask,
const std::vector<int64_t>& begin,
const std::vector<int64_t>& end,
const std::vector<int64_t>& strides,
const AxisSet& begin_mask,
const AxisSet& end_mask,
const AxisSet& new_axis_mask,
const AxisSet& shrink_axis_mask,
const AxisSet& ellipsis_mask);
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -618,9 +618,7 @@ def main():
t[3::-2] = None
t[4::-2] = None
t[5::-2] = None
# TODO(amprocte): Failing due to bug in DynReplaceSlice inference.
# Re-enable when NGCORE-510 is fixed.
#t[-9000:-8000:2] = None
t[-9000:-8000:2] = None
t[-9000:8000:2] = None
t[-5:5:2] = None
t[np.newaxis] = None
......@@ -639,9 +637,7 @@ def main():
t[0:100:2] = None
t[4:0:-2] = None
t[4:0:-3] = None
# TODO(amprocte): Failing due to bug in DynReplaceSlice inference.
# Re-enable when NGCORE-510 is fixed.
#t[3:2:1] = None
t[3:2:1] = None
t[4::-2] = None
#
......@@ -690,6 +686,35 @@ def main():
t[7:0:-3] = None
t[7::-3] = None
t.set_dtype('int32')
t.set_shape((4, 5))
t[2:4, ...] = None
t[4:2, ...] = None
t[4:2:-3, ...] = None
t[-100:100, ...] = None
t[..., 2:] = None
t[..., 2:4] = None
t[..., :] = None
t[..., -100:100] = None
t.set_shape((5, 6, 10, 8))
t[2:4, ..., 1:7:3, 7:2:-2] = None
t[..., 1:7:3, 7:2:-2] = None
t[2:4, ..., :3, -3:2:-2] = None
t[2:4, ..., 1:7:-3, 7:2:-2] = None
t[2:4, ..., :, np.newaxis, 0] = None
t.set_shape((2, 2, 3, 2, 3, 3))
t[2:6:2, ..., :, 2:1:-1] = None
t[np.newaxis, 1, ..., np.newaxis, 2:1:-1] = None
t[1, ..., np.newaxis, 2:1:-1] = None
t[np.newaxis, np.newaxis, 2:1:-1, ...] = None
t.set_shape((3, 3, 3, 2, 3))
t[6:1:-2, ..., 1:2, 2:1:-1] = None
t.set_shape((3, 3, 3, 2, 3))
t[..., 1:2, 2:1:-1] = None
t.set_dtype('int32')
t[80000] = None # error expected (shrink-axis OOB)
t[-80000] = None # error expected (shrink-axis OOB)
......
......@@ -513,7 +513,7 @@ INSTANTIATE_TEST_CASE_P(
/* Axis Masks: New, Shrink, Ellipsis */
DynReplaceSliceParams{{10}, {1}, {1}, {0}, {1, 10}, {0}, {10}, {}, {}, {}, {0}, {}, {}},
DynReplaceSliceParams{
{1, 2, 3}, {2}, {2}, {0}, {1, 2, 2}, {0, 0}, {1, 2}, {}, {}, {}, {}, {}, {1}},
{1, 2, 3}, {2}, {2}, {0}, {1, 2, 3}, {0, 0}, {1, 2}, {}, {}, {}, {}, {}, {1}},
DynReplaceSliceParams{{1, 2, 3},
{4},
{4},
......@@ -530,7 +530,53 @@ INSTANTIATE_TEST_CASE_P(
DynReplaceSliceParams{
{1, 2, 3}, {3}, {3}, {0}, {1, 1, 2, 1}, {0, 0, 1}, {2, 2, 2}, {}, {}, {}, {0}, {}, {1}},
DynReplaceSliceParams{
{1, 2, 2, 2}, {1}, {1}, {1}, {1, 2, 2}, {-1}, {0}, {-2}, {1}, {1}, {}, {1}, {}},
{1, 2, 2, 2}, {1}, {1}, {1}, {0, 2, 2, 2}, {-1}, {0}, {-2}, {1}, {1}, {}, {1}, {}},
DynReplaceSliceParams{{9, 10, 12, 2, 3}, /*arg_shape*/
{4}, /*lower_bounds_shape*/
{4}, /*upper_bounds_shape*/
{4}, /*strides_shape*/
{2, 10, 12, 2, 0}, /*replacement_shape*/
{2, 0, 0, 3}, /*lower_bounds_val*/
{6, 0, 0, 2}, /*upper_bounds_val*/
{2, 1, 1, -1}, /*strides_val*/
{2}, /*lower_bounds_mask*/
{2}, /*upper_bounds_mask*/
{}, /*new_axis*/
{}, /*shrink_axis*/
{1}}, /*ellipsis_mask*/
DynReplaceSliceParams{{9, 10, 12, 2, 3}, /*arg_shape*/
{4}, /*lower_bounds_shape*/
{4}, /*upper_bounds_shape*/
{4}, /*strides_shape*/
{3, 10, 12, 1, 0}, /*replacement_shape*/
{6, 0, 1, 3}, /*lower_bounds_val*/
{1, 0, 2, 2}, /*upper_bounds_val*/
{-2, 1, 1, -1}, /*strides_val*/
{}, /*lower_bounds_mask*/
{}, /*upper_bounds_mask*/
{}, /*new_axis*/
{}, /*shrink_axis*/
{1}}, /*ellipsis_mask*/
DynReplaceSliceParams{{9, 10, 12, 2, 3}, /*arg_shape*/
{3}, /*lower_bounds_shape*/
{3}, /*upper_bounds_shape*/
{3}, /*strides_shape*/
{9, 10, 12, 1, 0}, /*replacement_shape*/
{0, 1, 3}, /*lower_bounds_val*/
{0, 2, 2}, /*upper_bounds_val*/
{1, 1, -1}, /*strides_val*/
{}, /*lower_bounds_mask*/
{}, /*upper_bounds_mask*/
{}, /*new_axis*/
{}, /*shrink_axis*/
{0}}, /*ellipsis_mask*/
DynReplaceSliceParams{{1, 2, 2, 2},
{4},
{4},
......
......@@ -179,9 +179,8 @@ INSTANTIATE_TEST_CASE_P(
type_prop,
DeduceDynSliceTest,
::testing::Values(
// TODO(jbobba): These tests should pass.
// DynSliceParams({{4}, {1}, {1}, {1}, {0}}, {{-9000}, {-8000}, {2}}, {{}, {}, {}, {}, {}}),
// DynSliceParams({{5}, {1}, {1}, {1}, {0}}, {{3}, {2}, {1}}, {{}, {}, {}, {}, {}}),
DynSliceParams({{4}, {1}, {1}, {1}, {0}}, {{-9000}, {-8000}, {2}}, {{}, {}, {}, {}, {}}),
DynSliceParams({{5}, {1}, {1}, {1}, {0}}, {{3}, {2}, {1}}, {{}, {}, {}, {}, {}}),
DynSliceParams({{2, 3, 4, 5, 6}, {5}, {5}, {5}, {1, 2, 1, 1, 3}},
{{0, 1, 2, 3, 1}, {1, 3, 3, 5, 6}, {1, 1, 1, 2, 2}},
{{}, {}, {}, {}, {}}),
......@@ -205,7 +204,7 @@ INSTANTIATE_TEST_CASE_P(
DynSliceParams({{10}, {1}, {1}, {1}, {5}}, {{-1}, {0}, {-2}}, {{}, {0}, {}, {}, {}}),
// Axis Masks: New, Shrink, Ellipsis
DynSliceParams({{10}, {1}, {1}, {0}, {1, 10}}, {{0}, {10}, {}}, {{}, {}, {0}, {}, {}}),
DynSliceParams({{1, 2, 3}, {2}, {2}, {0}, {1, 2, 2}},
DynSliceParams({{1, 2, 3}, {2}, {2}, {0}, {1, 2, 3}},
{{0, 0}, {1, 2}, {}},
{{}, {}, {}, {}, {1}}),
DynSliceParams({{1, 2, 3}, {4}, {4}, {0}, {1, 2, 1}},
......@@ -214,7 +213,7 @@ INSTANTIATE_TEST_CASE_P(
DynSliceParams({{1, 2, 3}, {3}, {3}, {0}, {1, 1, 2, 1}},
{{0, 0, 1}, {2, 2, 2}, {}},
{{}, {}, {0}, {}, {1}}),
DynSliceParams({{1, 2, 2, 2}, {1}, {1}, {1}, {1, 2, 2}},
DynSliceParams({{1, 2, 2, 2}, {1}, {1}, {1}, {0, 2, 2, 2}},
{{-1}, {0}, {-2}},
{{1}, {1}, {}, {1}, {}}),
DynSliceParams({{1, 2, 2, 2}, {4}, {4}, {0}, {1, 2, 2}},
......@@ -222,7 +221,56 @@ INSTANTIATE_TEST_CASE_P(
{{1}, {1}, {}, {1}, {}}),
DynSliceParams({{1, 2, 3}, {3}, {3}, {0}, {1, 1, 2}},
{{0, 0, 1}, {2, 2, 2}, {}},
{{}, {}, {0}, {2}, {1}})));
{{}, {}, {0}, {2}, {1}}),
DynSliceParams({{9, 10, 12, 2, 3}, /*arg_shape*/
{4}, /*lower_bounds_shape*/
{4}, /*upper_bounds_shape*/
{4}, /*strides_shape*/
{2, 10, 12, 2, 0}}, /*replacement_shape*/
{{2, 0, 0, 3}, /*lower_bounds_val*/
{6, 0, 0, 2}, /*upper_bounds_val*/
{2, 1, 1, -1}}, /*strides_val*/
{{2}, /*lower_bounds_mask*/
{2}, /*upper_bounds_mask*/
{}, /*new_axis*/
{}, /*shrink_axis*/
{1}}), /*ellipsis_mask*/
DynSliceParams({{9, 10, 12, 2, 3}, /*arg_shape*/
{4}, /*lower_bounds_shape*/
{4}, /*upper_bounds_shape*/
{4}, /*strides_shape*/
{3, 10, 12, 1, 0}}, /*replacement_shape*/
{{6, 0, 1, 3}, /*lower_bounds_val*/
{1, 0, 2, 2}, /*upper_bounds_val*/
{-2, 1, 1, -1}}, /*strides_val*/
{{}, /*lower_bounds_mask*/
{}, /*upper_bounds_mask*/
{}, /*new_axis*/
{}, /*shrink_axis*/
{1}}), /*ellipsis_mask*/
DynSliceParams({{9, 10, 12, 2, 3}, /*arg_shape*/
{3}, /*lower_bounds_shape*/
{3}, /*upper_bounds_shape*/
{3}, /*strides_shape*/
{9, 10, 12, 1, 0}}, /*replacement_shape*/
{{0, 1, 3}, /*lower_bounds_val*/
{0, 2, 2}, /*upper_bounds_val*/
{1, 1, -1}}, /*strides_val*/
{{}, /*lower_bounds_mask*/
{}, /*upper_bounds_mask*/
{}, /*new_axis*/
{}, /*shrink_axis*/
{0}}) /*ellipsis_mask*/
));
void DynSlice_Test_Shape_Except(const shared_ptr<Node>& param_0,
const shared_ptr<Node>& param_1,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment