Files
openvino/model-optimizer/unit_tests/extensions/middle/LayoutChangeForEinsum_test.py
T
Roman Kazantsev 12fb83dc1e Correct layout for Einsum inputs and output (#6696)
* Fix recovery of output subscript in Einsum implicit mode

Signed-off-by: Roman Kazantsev <roman.kazantsev@intel.com>

* Fix code style

Signed-off-by: Roman Kazantsev <roman.kazantsev@intel.com>

* Correct layout adjustment for Einsum inputs and output

Signed-off-by: Roman Kazantsev <roman.kazantsev@intel.com>

* Correct a comment in the unit-test

Signed-off-by: Roman Kazantsev <roman.kazantsev@intel.com>

* Setup correct transformation dependencies for LayoutChangeForEinsum

Signed-off-by: Roman Kazantsev <roman.kazantsev@intel.com>
2021-07-23 11:01:06 +03:00

126 lines
7.3 KiB
Python

# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
import numpy as np
from extensions.middle.LayoutChangeForEinsum import LayoutChangeForEinsum
from mo.front.common.partial_infer.utils import int64_array
from mo.utils.ir_engine.compare_graphs import compare_graphs
from unit_tests.utils.graph import build_graph, result, regular_op_with_shaped_data, valued_const_with_data, connect
nodes_attributes = {
# Parameter layers
**regular_op_with_shaped_data('placeholder_1', None, {'type': 'Parameter', 'op': 'Parameter'}),
**regular_op_with_shaped_data('placeholder_2', None, {'type': 'Parameter', 'op': 'Parameter'}),
**regular_op_with_shaped_data('placeholder_3', None, {'type': 'Parameter', 'op': 'Parameter'}),
# Einsum layer
**regular_op_with_shaped_data('einsum', None, {'type': 'Einsum', 'op': 'Einsum'}),
# Result layer
**result(),
# Transpose layers
**regular_op_with_shaped_data('transpose_1', None,
{'type': 'Transpose', 'op': 'Transpose', 'need_shape_inference': True}),
**regular_op_with_shaped_data('transpose_3', None,
{'type': 'Transpose', 'op': 'Transpose', 'need_shape_inference': True}),
# Const layers
**valued_const_with_data('axis_1_const', int64_array([0, 2, 3, 1])),
**valued_const_with_data('axis_3_const', int64_array([0, 4, 1, 2, 3])),
}
class LayoutChangeForEinsumTests(unittest.TestCase):
def test_layout_change_einsum(self):
graph = build_graph(nodes_attributes,
[*connect('placeholder_1', '0:einsum'),
*connect('placeholder_2', '1:einsum'),
*connect('placeholder_3', '2:einsum'),
*connect('einsum', 'output')],
{ # this input stays as is since it is of a rank equal to 3
'placeholder_1_d': {'shape': np.array([2, 3, 5])},
# [3, 5, 7, 8] - NHWC, [3, 8, 5, 7] - NCHW
# this input does not require additional transpose
# since the corresponding subscript can be adjusted
'placeholder_2_d': {'shape': np.array([3, 8, 5, 7])},
# [3, 8, 10, 12] - NHWC, [3, 12, 8, 10] - NCHW
# the third input must be transposed to NHWC layout
# since ellipsis covers multiple dimensions in the end
# the corresponding subscript is not changed
'placeholder_3_d': {'shape': np.array([3, 12, 8, 10])},
# equation is still for NHWC layout
'einsum': {'equation': "abc,bcde,bc...->ade..."},
# [2, 7, 8, 10, 12] - NHWC, [2, 12, 7, 8, 10] - NCHW
# the output is in NCHW layout but its shape will be re-inferred since
# the output stays in NHWC layout due to ellipsis in the end
# and additional transpose to NCHW will be inserted
'einsum_d': {'shape': np.array([2, 12, 7, 8, 10])},
}, nodes_with_edges_only=True)
graph.graph['layout'] = 'NHWC'
graph_ref = build_graph(nodes_attributes,
[*connect('placeholder_3', '0:transpose_1'),
*connect('axis_1_const', '1:transpose_1'),
*connect('placeholder_1', '0:einsum'),
*connect('placeholder_2', '1:einsum'),
*connect('transpose_1', '2:einsum'),
*connect('einsum', '0:transpose_3'),
*connect('axis_3_const', '1:transpose_3'),
*connect('transpose_3', 'output')],
{'placeholder_1_d': {'shape': np.array([2, 3, 5])},
'placeholder_2_d': {'shape': np.array([3, 8, 5, 7])},
'einsum': {'equation': "abc,becd,bc...->ade..."},
'einsum_d': {'shape': np.array([2, 12, 7, 8, 10])}
})
LayoutChangeForEinsum().find_and_replace_pattern(graph)
(flag, resp) = compare_graphs(graph, graph_ref, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)
def test_no_adjustment_layout_einsum(self):
graph = build_graph(nodes_attributes,
[*connect('placeholder_1', '0:einsum'),
*connect('placeholder_2', '1:einsum'),
*connect('placeholder_3', '2:einsum'),
*connect('einsum', 'output')],
{ # this input stays as is since it is of a rank equal to 3
'placeholder_1_d': {'shape': np.array([2, 3, 5])},
# [3, 5, 7, 8] - NHWC
# this input does not require additional transpose
# since the corresponding layout is correct
'placeholder_2_d': {'shape': np.array([3, 5, 7, 8])},
# [3, 8, 10, 12] - NHWC
# this input does not require additional transpose
# since the corresponding layout is correct
'placeholder_3_d': {'shape': np.array([3, 8, 10, 12])},
# equation is still for NHWC layout
'einsum': {'equation': "abc,bcde,bc...->ade...",
'correct_in_data_layout': [0, 1, 2],
'correct_out_data_layout': [0]},
# [2, 7, 8, 10, 12] - NHWC
# this output does not require additional transpose
# since the corresponding layout is correct
'einsum_d': {'shape': np.array([2, 7, 8, 10, 12])},
}, nodes_with_edges_only=True)
graph.graph['layout'] = 'NHWC'
graph_ref = build_graph(nodes_attributes,
[*connect('placeholder_1', '0:einsum'),
*connect('placeholder_2', '1:einsum'),
*connect('placeholder_3', '2:einsum'),
*connect('einsum', 'output')],
{'placeholder_1_d': {'shape': np.array([2, 3, 5])},
'placeholder_2_d': {'shape': np.array([3, 5, 7, 8])},
'placeholder_3_d': {'shape': np.array([3, 8, 10, 12])},
'einsum': {'equation': "abc,bcde,bc...->ade..."},
'einsum_d': {'shape': np.array([2, 7, 8, 10, 12])}
})
LayoutChangeForEinsum().find_and_replace_pattern(graph)
(flag, resp) = compare_graphs(graph, graph_ref, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)