Skip to content

Commit

Permalink
added more unittest cases
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-esir committed Jul 20, 2020
1 parent e256f84 commit a76ebcf
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,46 @@
import unittest

import numpy as np
from generator import generator, generate

from extensions.front.onnx.AttributedSliceToSlice import AttributedSliceToSliceReplacer
from mo.utils.ir_engine.compare_graphs import compare_graphs
from mo.utils.unittest.graph import build_graph, regular_op_with_empty_data, result, const

nodes = {
**regular_op_with_empty_data('input', {'type': 'Parameter'}),
**regular_op_with_empty_data('attributed_slice', {'op': 'AttributedSlice', 'type': None,
# todo: add test for the case when does not have axes attribute
# 'start': np.array([0, 0]), 'end': np.array([1, -1]), 'axis': np.array([0, 1])}),
'starts': np.array([0, 0]), 'ends': np.array([1, -1])}),
**result(),

# nodes after replacement
**const('start', np.array([0, 0])),
**const('end', np.array([1, -1])),
**const('axis', np.array(np.array([0, 1]))),
**regular_op_with_empty_data('slice', {'op': 'Slice', 'type': None}),
}
from mo.utils.unittest.graph import build_graph, regular_op_with_empty_data, result, const, connect_on_front


@generator
class SliceReplacerTest(unittest.TestCase):
@generate(*[
{'op': 'AttributedSlice', 'type': None, 'starts': np.array([0, 0]), 'ends': np.array([1, -1])},
{'op': 'AttributedSlice', 'type': None, 'starts': np.array([0, 0]), 'ends': np.array([1, -1]), 'axis': np.array([0, 1])}
])
def test_attributed_slice_replacer(self, attributed_slice_attrs):
nodes = {
**regular_op_with_empty_data('input', {'type': 'Parameter'}),
**regular_op_with_empty_data('attributed_slice', attributed_slice_attrs),
**result(),

# nodes after replacement
**const('start', np.array([0, 0])),
**const('end', np.array([1, -1])),
**const('axis', np.array(np.array([0, 1]))),
**regular_op_with_empty_data('slice', {'op': 'Slice', 'type': None}),
}

def test_attributed_slice_replacer(self):
graph = build_graph(nodes_attrs=nodes, edges=[
('input', 'attributed_slice', {'out': 0}),
('attributed_slice', 'output', {'out': 0}),
('input', 'attributed_slice'),
('attributed_slice', 'output'),
], nodes_with_edges_only=True)
graph.stage = 'front'

AttributedSliceToSliceReplacer().find_and_replace_pattern(graph)

graph_ref = build_graph(nodes_attrs=nodes, edges=[
('input', 'slice', {'out': 0}),

('start', 'slice', {'out': 0, 'in': 1}),
('end', 'slice', {'out': 0, 'in': 2}),
('axis', 'slice', {'out': 0, 'in': 3}),

('slice', 'output', {'out': 0}),
('input', 'slice'),
*connect_on_front('start', '1:slice'),
*connect_on_front('end', '2:slice'),
*connect_on_front('axis', '3:slice'),
('slice', 'output'),
], nodes_with_edges_only=True)

(flag, resp) = compare_graphs(graph, graph_ref, 'output', check_op_attrs=True)
Expand Down
73 changes: 34 additions & 39 deletions model-optimizer/extensions/front/tf/TFSliceToSlice_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from extensions.front.tf.TFSliceToSlice import TFSliceToSliceReplacer
from mo.utils.ir_engine.compare_graphs import compare_graphs
from mo.utils.unittest.graph import build_graph, regular_op_with_empty_data, result, const
from mo.utils.unittest.graph import build_graph, regular_op_with_empty_data, result, const, connect_on_front

nodes = {
**regular_op_with_empty_data('input', {'type': 'Parameter'}),
Expand All @@ -41,70 +41,65 @@


class SliceReplacerTest(unittest.TestCase):

# test case when input goes besides from TFSlice to other nodes
def test_slice_replacer_begin_with_2_inputs(self):
graph = build_graph(nodes_attrs=nodes, edges=[
('input', 'tfslice', {'out': 0}),
('begin', 'tfslice', {'out': 0, 'in': 1}),
('begin', 'john_doe', {'out': 0, 'in': 0}),
('size', 'tfslice', {'out': 0, 'in': 2}),
('tfslice', 'output', {'out': 0}),
('input', 'tfslice'),
*connect_on_front('begin:0', '1:tfslice'),
*connect_on_front('begin:0', '0:john_doe'),
*connect_on_front('size:0', '2:tfslice'),
*connect_on_front('tfslice:0', 'output'),
], nodes_with_edges_only=True)
graph.stage = 'front'

TFSliceToSliceReplacer().find_and_replace_pattern(graph)

graph_ref = build_graph(nodes_attrs=nodes, edges=[
('input', 'slice', {'out': 0}),
('begin', 'slice', {'out': 0, 'in': 1}),
('begin', 'john_doe', {'out': 0, 'in': 1}),
*connect_on_front('input:0', 'slice'),
*connect_on_front('begin:0', 'slice:1'),
*connect_on_front('begin:0', 'john_doe:1'),

('begin', 'end_const', {'out': 0, 'in': 0}),
('size', 'end_const', {'out': 0, 'in': 1}),
('size', 'equal', {'out': 0, 'in': 0}),
*connect_on_front('begin:0', 'end_const:0'),
*connect_on_front('size:0', 'end_const:1'),
*connect_on_front('size:0', 'equal:0'),

('int32_max', 'select', {'out': 0, 'in': 1}),
('minus_one', 'equal', {'out': 0, 'in': 1}),
*connect_on_front('int32_max:0', 'select:1'),
*connect_on_front('minus_one:0', 'equal:1'),

('equal', 'select', {'out': 0, 'in': 0}),
*connect_on_front('equal:0', 'select:0'),

('end_const', 'select', {'out': 0, 'in': 2}),
('select', 'slice', {'out': 0, 'in': 2}),
*connect_on_front('end_const:0', 'select:2'),
*connect_on_front('select:0', 'slice:2'),

('slice', 'output', {'out': 0}),
*connect_on_front('slice:0', 'output'),
], nodes_with_edges_only=True)

(flag, resp) = compare_graphs(graph, graph_ref, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)

def test_slice_replacer(self):
graph = build_graph(nodes_attrs=nodes, edges=[
('input', 'tfslice', {'out': 0}),
('begin', 'tfslice', {'out': 0, 'in': 1}),
('size', 'tfslice', {'out': 0, 'in': 2}),
('tfslice', 'output', {'out': 0}),
*connect_on_front('input:0', 'tfslice'),
*connect_on_front('begin:0', '1:tfslice'),
*connect_on_front('size:0', '2:tfslice'),
*connect_on_front('tfslice:0', 'output'),
], nodes_with_edges_only=True)
graph.stage = 'front'

TFSliceToSliceReplacer().find_and_replace_pattern(graph)

graph_ref = build_graph(nodes_attrs=nodes, edges=[
('input', 'slice', {'out': 0}),
('begin', 'slice', {'out': 0, 'in': 1}),

('begin', 'end_const', {'out': 0, 'in': 0}),
('size', 'end_const', {'out': 0, 'in': 1}),
('size', 'equal', {'out': 0, 'in': 0}),

('int32_max', 'select', {'out': 0, 'in': 1}),
('minus_one', 'equal', {'out': 0, 'in': 1}),

('equal', 'select', {'out': 0, 'in': 0}),

('end_const', 'select', {'out': 0, 'in': 2}),
('select', 'slice', {'out': 0, 'in': 2}),

('slice', 'output', {'out': 0}),
*connect_on_front('input:0', 'slice'),
*connect_on_front('begin:0', '1:slice'),
*connect_on_front('begin:0', '0:end_const'),
*connect_on_front('size:0', '1:end_const'),
*connect_on_front('size:0', '0:equal'),
*connect_on_front('int32_max:0', '1:select'),
*connect_on_front('minus_one:0', '1:equal'),
*connect_on_front('equal:0', '0:select'),
*connect_on_front('end_const:0', '2:select'),
*connect_on_front('select:0', '2:slice'),
*connect_on_front('slice:0', 'output'),
], nodes_with_edges_only=True)

(flag, resp) = compare_graphs(graph, graph_ref, 'output', check_op_attrs=True)
Expand Down
3 changes: 0 additions & 3 deletions model-optimizer/mo/ops/slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,6 @@ def infer(node: Node):

if input_value is None:
output_shape = get_shape_after_slice(input_shape, slice_idx)
if np.any(output_shape == 0):
# todo: add unittest for this case
raise Error("Output shape ({}) for Slice node {} has zero elements".format(output_shape, node.name))
node.out_port(0).data.set_shape(output_shape)
else:
node.out_port(0).data.set_value(input_value[tuple(slice_idx)])
99 changes: 68 additions & 31 deletions model-optimizer/mo/ops/slice_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,49 +21,39 @@
from mo.front.common.partial_infer.utils import int64_array
from mo.graph.graph import Node
from mo.ops.slice import Slice
from mo.utils.error import Error
from mo.utils.unittest.graph import build_graph, valued_const_with_data, valued_data, regular_op_with_empty_data, \
connect
connect, shaped_data, shaped_const_with_data


@generator
class TestSliceOp(unittest.TestCase):
# todo: add this case through @generate and remove
# def test_slice_infer_non_constant(self):
# # Testing non-constant path case (when value in input is None)
# # with multiply params
# graph = build_graph(nodes_attributes,
# [('data_1', 'slice'),
# ('starts', 'slice'),
# ('ends', 'slice'),
# ('axes', 'slice'),
# ('slice', 'data_2')],
# {'data_1': {'shape': np.array([4, 5, 6])},
# 'starts': {'value': np.array([1, 2])},
# 'ends': {'value': np.array([4, 3])},
# 'axes': {'value': np.array([0, 1])}})
#
# slice_node = Node(graph, 'slice')
#
# Slice.infer(slice_node)
# self.assertTrue(np.array_equal(slice_node.out_node().value, None))
# self.assertTrue(np.array_equal(slice_node.out_node().shape, np.array([3, 1, 6])))
#

@generate(*[
# standard case
([[4, 5, 6, 7], [2, 3, 5, 6], [5, 6, 8, 9], [5, 6, 8, 9]], [0, 1], [3, 2], [0, 1], [1, 1], [[5], [3], [6]]),
# negative bounds
([[4, 5, 6, 7], [2, 3, 5, 6], [5, 6, 8, 9], [5, 6, 8, 9]], [0, 1], [3, -2], [0, 1], [1, 1], [[5], [3], [6]]),
# unusual order of axes
([[4, 5, 6, 7], [2, 3, 5, 6], [5, 6, 8, 9], [5, 6, 8, 9]], [0, 1], [3, -2], [1, 0], [1, 1], [[2, 3, 5]]),
# second case
([1, 3, 224, 224], [1], [2], [0], [1], [3])
# # third case
# ()
# when only input_shape is defined without values (one from buttom element is shape)
(None, [1, 2], [4, 3], [0, 1], [1, 1], [3, 1, 6], [4, 5, 6]),
# boundary case
(None, [0, 2], [np.iinfo(np.int32).max, 3], [0, 1], [1, 1], [4, 1, 6], [4, 5, 6]),
# boundary case
(None, [np.iinfo(np.int32).min, 2], [3, 3], [0, 1], [1, 1], [3, 1, 6], [4, 5, 6]),
# 1D input
([1, 3, 224, 224], [1], [2], [0], [1], [3]),
# case when output shape has zero elements
(None, [1], [1], [0], [1], [0], [4])
])
def test_slice_infer(self, inp_value, starts, ends, axes, steps, expected, inp_shape=None):
inp_shape = int64_array(inp_value).shape if inp_value is not None else inp_shape
inp_value = int64_array(inp_value) if inp_value is not None else inp_value
if inp_value is None:
input_node = shaped_data('data_1', int64_array(inp_shape))
else:
input_node = valued_data('data_1', int64_array(inp_value))

nodes = {
**valued_data('data_1', inp_value),
**input_node,
**regular_op_with_empty_data('slice', {'op': 'Slice'}),
**valued_const_with_data('starts', int64_array(starts)),
**valued_const_with_data('ends', int64_array(ends)),
Expand All @@ -83,4 +73,51 @@ def test_slice_infer(self, inp_value, starts, ends, axes, steps, expected, inp_s
slice_node = Node(graph, 'slice')

Slice.infer(slice_node)
self.assertTrue(np.array_equal(slice_node.out_node().value, expected))
if inp_value is not None:
self.assertTrue(np.array_equal(slice_node.out_node().value, expected))
else:
self.assertTrue(np.array_equal(slice_node.out_node().shape, expected))

# negative tests
@generate(*[
# starts are non-constant
([[4, 5, 6, 7], [2, 3, 5, 6], [5, 6, 8, 9], [5, 6, 8, 9]], None, [3, 2], [0, 1], [1, 1], [[5], [3], [6]]),
# ends are non-constant
([[4, 5, 6, 7], [2, 3, 5, 6], [5, 6, 8, 9], [5, 6, 8, 9]], [0, 1], None, [0, 1], [1, 1], [[5], [3], [6]]),
# axes are non-constant
([[4, 5, 6, 7], [2, 3, 5, 6], [5, 6, 8, 9], [5, 6, 8, 9]], [0, 1], [3, -2], None, [1, 1], [[5], [3], [6]]),
# steps are non-constant
([[4, 5, 6, 7], [2, 3, 5, 6], [5, 6, 8, 9], [5, 6, 8, 9]], [0, 1], [3, -2], [0, 1], None, [[5], [3], [6]]),
])
def test_slice_infer_negative(self, inp_value, starts, ends, axes, steps, expected, inp_shape=None):
if inp_value is None:
input_node = shaped_data('data_1', int64_array(inp_shape))
else:
input_node = valued_data('data_1', int64_array(inp_value))

def convert_args(val, name=''):
if val is not None:
return valued_const_with_data(name, int64_array(val))
else:
return shaped_const_with_data(name, [0]) #fake shape

starts = convert_args(starts, 'starts')
ends = convert_args(ends, 'ends')
axes = convert_args(axes, 'axes')
steps = convert_args(steps, 'steps')

nodes = { **input_node,
**regular_op_with_empty_data('slice', {'op': 'Slice'}),
**starts, **ends, **axes, **steps }

graph = build_graph(nodes,
[('data_1', 'slice'),
*connect('starts', '1:slice'),
*connect('ends', '2:slice'),
*connect('axes', '3:slice'),
*connect('steps', '4:slice'),
*connect('slice', 'slice_d')])

graph.stage = 'middle'
slice_node = Node(graph, 'slice')
self.assertRaises(Error, Slice.infer, slice_node)
12 changes: 12 additions & 0 deletions model-optimizer/mo/utils/unittest/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ def build_graph(nodes_attrs: dict, edges: list, update_attributes: dict = None,
# Add in_ports attribute
in_edges = node.in_edges()
for attr in in_edges.values():
# node.add_input_port(idx=attr['in'], skip_if_exist=True)
node.add_input_port(idx=attr['in'])

# Add out_ports attribute
Expand Down Expand Up @@ -318,3 +319,14 @@ def connect(first_tensor_name, second_tensor_name, skip_data=False):

def connect_data(first_tensor_name, second_tensor_name):
return connect(first_tensor_name, second_tensor_name, skip_data=True)


def connect_on_front(first_tensor_name, second_tensor_name, skip_data=False):
# ports could be skipped -- then zero in/out ports would be used
# first_tensor_name = first_op_name:out_port
# second_tensor_name = in_port:second_op_name

first_op_name, out_port = get_name_and_port(first_tensor_name)
second_op_name, in_port = get_name_and_port(second_tensor_name)

return [(first_op_name, second_op_name, {'out': out_port, 'in': in_port})]

0 comments on commit a76ebcf

Please sign in to comment.