MO support for operations DFT and IDFT (#5197)

* Written MO classes for DFT and IDFT operations.

* Added class to read TF (I)FFT operations.

* Written extractors for TF operations FFT, FFT2D, FFT3D, IFFT, IFFT2D, IFFT3D.

* Written MO Roll operation and TF Roll operation extractor.

* Started to write needed transformations.

* Written transformation StridedSlices + Complex + Roll + (i)FFTxD + Roll + (Imag, Real) + Pack -> Roll + (I)DFT + Roll.

* Written transformation for Complex + ComplexAbs.

* Written correction of axes of Roll.

* Small fix.

* Small fix.

* Some fixes.

* Some changes.

* Now TF Roll is read as TFRoll. Written inserting Transposes before and after (I)DFT.

* Small fix.

* Written tests for the transformation TFRollToRoll.

* Added comments to some transformations.

* Deleted redundant import.

* Written tests for the transformation TransposeDFT.

* Fixes in MO IR Reader to read/write (I)DFT.

* Fixes in the list of supported TF layers.

* Started to write tests for SSliceComplexRolledFFTPackBlockReplacement transformation.

* Written tests for the MO transformation SSliceComplexRolledFFTPackBlockReplacement.

* Written tests for the MO transformation ComplexAbs.

* Tests for transformations were moved into unit_tests directory.

* All extractors for (I)FFTxD are in one file now.

* Deleted redundant transformations.

* Fixed extractor for TF Roll: now this operation is read as MO Roll.

* Added comments to TFFFT operation.

* The method insert_transpose of classes TransposeDFT and LayoutChangeForGatherND was moved into the separate function in the file model-optimizer/extensions/middle/InsertLayoutPropagationTransposes.py.

* Fixed comment for the transformation TransposeDFT.

* Small fix.

* Some fixes.

* Deleted shape infer function for the operation TFFFT. Sorted imports in complex_abs.py.

* Small fixes.

* Deleted redundant import.

* Fixes in some asserts.

* Small fix.

* Added names for created nodes in the transformation ComplexAbs.

* Added comments to the method canonicalize_axes.

* The transformation SSliceComplexRolledFFTPackBlockReplacement was split into the sequence of transformations SSliceComplexRollReplacement -> RollRealImagPackReplacement -> TFFFTToDFT.

* Written tests for the transformation SSliceComplexRollReplacement.

* Written tests for the transformation RollRealImagPackReplacement.

* Written tests for the transformation TFFFTToDFT.

* Deleted commented code.

* Fixed types of constants in the transformation ComplexAbs.

* Written tests for canonicalization of signal_size value.

* Deleted 'Replacement' from names of files and classes.

* Used comarison of ids, not names.

* replace_sub_graph was replaced with find_and_replace_pattern.

* Now the transformation RollRealImagPack is executed before running transformation model-optimizer/extensions/front/Pack.py.

* The body of the function create_dft_from_tffft is a part of the transformation TFFFTToDFT body now.

* Now method correct_roll_axes of classes RollRealImagPack and SSliceComplexRoll is moved to the function in mo/front/tf/graph_utils.py.

* Small changes.

* Added comment before mark_input_as_in_correct_layout(roll, 2).

* Now the functions correct_roll_axes generates sub-graph in the input port 2 of Roll.

* Corrected tests for the transformation SSliceComplexRoll.

* Corrected tests for the transformation RollRealImagPack.

* Deleted commented code.

* Some renaming.

* Added decomposition of the separate operation ComplexAbs (without Complex before it).

* Added comment to the transformation ComplexAbsAfterComplex.

* Optimized imports for the transformation TFFFTToDFT.

* The transformation SSliceComplexRoll was split into the sequence SSliceComplex -> CorrectRollAxes and disabled.

* Written tests for the transformation ComplexAbs.

* Written tests for the transformation SSliceComplex.

* Written tests for the transformation CorrectRollAxes.

* Deleted the transformation SSliceComplexRoll.

* Deleted renaming nodes.

* Fixed comment.

* Small fixes.

* Small fix.

* The attribute need_correction was renamed as input_rank_changed.

* Small fixes.

* Deleted commented code.

* Now we iterate over all complex_node.out_port(0).get_connection().get_destinations() input ports and mark the corresponding nodes with the marker attribute.

* Added the attribute 'in_ports_count' into the class FFTBase.

* Tests for the transformation TransposeDFT were rewritten using helper functions.

* Now the transformation RollRealImagPack uses existing Roll node instead of creating new one.

* Small fixes.

* Fix in the documentation.

* Written class to read MxNet (I)FFT operations. Written corresponding extractors.

* Corrected shape infer function for MXFFT operation. Written transformation to convert MXFFT to (I)DFT.

* Fixed shape infer function.

* Fixed the conversion MXFFT to (I)DFT.

* Written tests for the transformation MXFFTToDFT.

* The function correct_roll_axes was replaced with more generic function add_constant_to_negative_values.

* Fixes in classes TFFFT, FFTBase, DFT, IDFT, MXFFT.

* Added asserts in constructors of operations TFFFT and MXFFT.

* Refactored transformation MXFFTToDFT: conversion of DFT and IDFT were moved into separated functions.

* Moved some commented code.

* Fixed BOM file.

* Written function convert_ifft_to_dft.

* Started to rewrite tests for MXFFTToDFT transformations, in the case is_inverse=False.

* Small fixes.

* Fixes in the transformation RollRealImagPack.

* Renaming tests class for the transformation SSliceComplex.

* Fixes in the function compare_graphs. Now we get all output nodes of op node, and these output nodes are sorted by names.

* Fixed tests for the transformation MXFFTToDFT.

* Fix in the transformation ThresholdedReluDecomposition: added disconnect for trelu input port.

* Fixes in test for the transformation TFSliceToSlice.

* Small fix in the transformation ObjectDetectionAPIPreprocessor2Replacement.

* Small fix in comment.

* Optimized imports.

* Used remove_node in the transformation ThresholdedReluDecomposition and remove_nodes_from in the transformation RollRealImagPack, instead of ports disconnection.

* Deleted commented code.

* Deleted test case test_slice_replacer_begin_with_2_inputs.
This commit is contained in:
Vladimir Gavrilov authored and GitHub committed 2021-05-07 09:44:24 +03:00
1 parent 8645c08396
commit 0c288d506c
34 files changed
+1729 -77

No files matched your search

+2
View File
@@ -139,6 +139,8 @@ for more details and command line parameters used for the model conversion.
```bash
./<MO_INSTALL_DIR>/mo.py --input_model <PATH_TO_MODEL>/wnet_20.pb -b 1
```
> **NOTE:** This conversion guide is applicable for the 2021.3 release of OpenVINO and that starting from 2021.4
> the OpenVINO supports this model out of the box.
Model Optimizer produces the following error:
```bash
@@ -160,6 +160,9 @@ Standard TensorFlow\* operations:
| EuclideanNorm | No |
| FakeQuantWithMinMaxVars | No |
| FakeQuantWithMinMaxVarsPerChannel | No |
| FFT | Supported only when it is part of a sub-graph of the special form |
| FFT2D | Supported only when it is part of a sub-graph of the special form |
| FFT3D | Supported only when it is part of a sub-graph of the special form |
| Fill | No |
| Floor | No |
| FloorDiv | No |
@@ -172,6 +175,9 @@ Standard TensorFlow\* operations:
| Greater | No |
| GreaterEqual | No |
| Identity | Not needed for shape inference |
| IFFT | Supported only when it is part of a sub-graph of the special form |
| IFFT2D | Supported only when it is part of a sub-graph of the special form |
| IFFT3D | Supported only when it is part of a sub-graph of the special form |
| LRN | No |
| Less | No |
| Log | No |
@@ -60,6 +60,7 @@ extensions/back/ShufflenetReLUReorder.py
extensions/back/SpecialNodesFinalization.py
extensions/back/StridedSliceMasksNormalizer.py
extensions/back/TopKNormalizer.py
extensions/back/TransposeDFT.py
extensions/back/TransposeReduceFusing.py
extensions/back/UselessConcatRemoval.py
extensions/front/__init__.py
@@ -196,6 +197,7 @@ extensions/front/mxnet/elementwise_ext.py
extensions/front/mxnet/eltwise_scalar_replacers.py
extensions/front/mxnet/exp_ext.py
extensions/front/mxnet/expand_dims_ext.py
extensions/front/mxnet/fft_ext.py
extensions/front/mxnet/flatten_ext.py
extensions/front/mxnet/fully_connected_ext.py
extensions/front/mxnet/gather.py
@@ -208,6 +210,7 @@ extensions/front/mxnet/max_ext.py
extensions/front/mxnet/multibox_detection_ext.py
extensions/front/mxnet/mx_reshape_reverse.py
extensions/front/mxnet/mx_reshape_to_reshape.py
extensions/front/mxnet/MXFFTToDFT.py
extensions/front/mxnet/MXRepeatReplacer.py
extensions/front/mxnet/null_ext.py
extensions/front/mxnet/pad_ext.py
@@ -383,10 +386,13 @@ extensions/front/tf/broadcast_ext.py
extensions/front/tf/bucketize.py
extensions/front/tf/bucketize_ext.py
extensions/front/tf/Cast_ext.py
extensions/front/tf/ComplexAbs.py
extensions/front/tf/ComplexAbsAfterComplex.py
extensions/front/tf/concat.py
extensions/front/tf/concat_ext.py
extensions/front/tf/const_ext.py
extensions/front/tf/conv_ext.py
extensions/front/tf/CorrectRollAxes.py
extensions/front/tf/crop_and_resize_ext.py
extensions/front/tf/CropAndResizeReplacement.py
extensions/front/tf/CTCGreedyDecoder_ext.py
@@ -413,6 +419,7 @@ extensions/front/tf/faster_rcnn_support_api_v1.15.json
extensions/front/tf/faster_rcnn_support_api_v1.7.json
extensions/front/tf/faster_rcnn_support_api_v2.0.json
extensions/front/tf/faster_rcnn_support_api_v2.4.json
extensions/front/tf/fft_ext.py
extensions/front/tf/fifo_queue_v2_ext.py
extensions/front/tf/fifo_replacer.py
extensions/front/tf/fill_ext.py
@@ -471,6 +478,7 @@ extensions/front/tf/rfcn_support_api_v1.10.json
extensions/front/tf/rfcn_support_api_v1.13.json
extensions/front/tf/rfcn_support_api_v1.14.json
extensions/front/tf/roll_ext.py
extensions/front/tf/RollRealImagPack.py
extensions/front/tf/select_ext.py
extensions/front/tf/sign_ext.py
extensions/front/tf/SizeReplacer.py
@@ -495,12 +503,14 @@ extensions/front/tf/ssd_toolbox_detection_output.json
extensions/front/tf/ssd_toolbox_multihead_detection_output.json
extensions/front/tf/ssd_v2_support.json
extensions/front/tf/SSDToolboxDetectionOutput.py
extensions/front/tf/SSliceComplex.py
extensions/front/tf/swap_deconv_inputs.py
extensions/front/tf/swish_ext.py
extensions/front/tf/SwitchMergeOptimization.py
extensions/front/tf/TensorArrayExtractors.py
extensions/front/tf/TensorArrayGatherV3.py
extensions/front/tf/tensorflow_custom_operations_config_update.py
extensions/front/tf/TFFFTToDFT.py
extensions/front/tf/TFResizeToInterpolate.py
extensions/front/tf/TFSliceToSlice.py
extensions/front/tf/tile_ext.py
@@ -667,6 +677,7 @@ extensions/ops/depth_to_space.py
extensions/ops/dequantize_linear.py
extensions/ops/DetectionOutput.py
extensions/ops/detectionoutput_onnx.py
extensions/ops/dft.py
extensions/ops/elementwise.py
extensions/ops/embedding_bag.py
extensions/ops/Enter.py
@@ -695,6 +706,7 @@ extensions/ops/lstm_sequence.py
extensions/ops/MatMul.py
extensions/ops/merge.py
extensions/ops/mvn.py
extensions/ops/mxfft.py
extensions/ops/mxrepeat.py
extensions/ops/mxreshape.py
extensions/ops/NextIteration.py
@@ -760,6 +772,7 @@ extensions/ops/TensorArrayScatter.py
extensions/ops/TensorArraySize.py
extensions/ops/TensorArrayWrite.py
extensions/ops/TensorIterator_ops.py
extensions/ops/TFFFT.py
extensions/ops/TFResize.py
extensions/ops/topk.py
extensions/ops/topkrois_onnx.py
@@ -1,13 +1,8 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import numpy as np
from extensions.ops.transpose import Transpose
from mo.front.common.partial_infer.utils import int64_array
from mo.front.tf.graph_utils import create_op_with_const_inputs
from mo.graph.graph import Graph, Port
from mo.back.replacement import BackReplacementPattern
from mo.graph.graph import Graph
class LayoutChangeForGatherND(BackReplacementPattern):
@@ -19,31 +14,9 @@ class LayoutChangeForGatherND(BackReplacementPattern):
force_shape_inference = True
graph_condition = [lambda graph: graph.graph['fw'] == 'tf']
@staticmethod
def insert_transpose(graph: Graph, input_port: Port, before_input=True):
input_rank = len(input_port.data.get_shape())
if input_rank > 3:
if before_input:
axis_order = np.concatenate((int64_array([0]),
int64_array(list(range(2, input_rank))),
int64_array([1])))
source_node = input_port.get_source().node
transpose_name = source_node.soft_get('name', source_node.id) + '/TransposeToNHWC'
else:
axis_order = np.concatenate(
(int64_array([0]),
int64_array([input_rank - 1]),
int64_array(list(range(1, input_rank - 1)))))
transpose_name = input_port.node.soft_get('name', input_port.node.id) + '/TransposeToNCHW'
input_port.node['need_shape_inference'] = True
input_port.node['override_output_shape'] = True
transpose = create_op_with_const_inputs(graph, Transpose, {1: axis_order}, {'name': transpose_name})
input_port.get_connection().insert_node(transpose)
transpose['need_shape_inference'] = True
transpose['override_output_shape'] = True
def find_and_replace_pattern(self, graph: Graph):
import extensions.middle.InsertLayoutPropagationTransposes as InsertTransposes
for gathernd in graph.get_op_nodes(type='GatherND'):
self.insert_transpose(graph, gathernd.in_port(0), before_input=True)
self.insert_transpose(graph, gathernd.in_port(1), before_input=True)
self.insert_transpose(graph, gathernd.out_port(0), before_input=False)
InsertTransposes.insert_transpose(graph, gathernd.in_port(0), before_input=True)
InsertTransposes.insert_transpose(graph, gathernd.in_port(1), before_input=True)
InsertTransposes.insert_transpose(graph, gathernd.out_port(0), before_input=False)
@@ -0,0 +1,34 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from mo.back.replacement import BackReplacementPattern
from mo.graph.graph import Graph
class TransposeDFT(BackReplacementPattern):
"""
In TF models, operation (I)FFTxD has some input shape, [N_0, ..., N_{r - 1}].
After the transformation SSliceComplexRolledFFTPackBlockReplacement, we have an input shape [N_0, ..., N_{r - 1}, 2]
for operation DFT or IDFT.
If the input rank in the TF model was greater than 2, we have [N_0, 2, N_1, ..., N_{r - 1}] as the input shape of
(I)DFT after the layout conversion, if the option '--disable_nhwc_to_nchw' is not specified.
But, generally speaking, according to DFT and IDFT specifications, the input shape [N_0, 2, N_1, ..., N_{r - 1}]
is not correct input shape for DFT and IDFT. Hence, we need to insert Transpose operations before and after (I)DFT
in such cases.
This transformation inserts such Transpose nodes, when the source model was the TF model, (I)DFT node has the
attribute 'need_insert_transposes_for_dft', and this attribute is True.
"""
enabled = True
force_shape_inference = True
graph_condition = [lambda graph: graph.graph['fw'] == 'tf']
def find_and_replace_pattern(self, graph: Graph):
import extensions.middle.InsertLayoutPropagationTransposes as InsertTransposes
for dft in graph.get_op_nodes(need_insert_transposes_for_dft=True):
InsertTransposes.insert_transpose(graph, dft.in_port(0), before_input=True)
InsertTransposes.insert_transpose(graph, dft.out_port(0), before_input=False)
@@ -36,3 +36,4 @@ class ThresholdedReluDecomposition(FrontReplacementPattern):
mul.in_port(1).connect(float_greater.out_port(0))
rename_nodes([(node, name + '/TBR'), (mul, name)])
graph.remove_node(node.id)
@@ -0,0 +1,140 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import numpy as np
from extensions.ops.dft import DFT, IDFT
from extensions.ops.elementwise import Add, Sub
from extensions.ops.rank import Rank
from extensions.ops.scatter import ScatterUpdate
from extensions.ops.split import Split
from mo.front.common.partial_infer.utils import int64_array
from mo.front.tf.graph_utils import create_op_with_const_inputs
from mo.graph.graph import Graph, Node, rename_nodes
from mo.front.common.replacement import FrontReplacementSubgraph
from mo.ops.broadcast import Broadcast
from mo.ops.concat import Concat
from mo.ops.pad import Pad
from mo.ops.reshape import Reshape
from mo.ops.squeeze import Squeeze
from mo.ops.unsqueeze import Unsqueeze
class MXFFTToDFT(FrontReplacementSubgraph):
"""
This transformation converts the operation MXFFT into OpenVINO DFT (if the attribute 'is_inverse' is False),
or into OpenVINO IDFT (otherwise).
According to https://mxnet.apache.org/versions/1.0.0/api/python/symbol/contrib.html#mxnet.symbol.contrib.fft,
MxNet operation FFT accept 2 input data shapes: [N, d] or [N_1, N_2, N_3, d], data can only be real numbers.
The output data has shape: [N, 2*d] or [N_1, N_2, N_3, 2*d]. The format is: [real0, imag0, real1, imag1, ...].
Next, MxNet operation IFFT accept 2 input data shapes: [N, d] or [N_1, N_2, N_3, d]. Data is in format:
[real0, imag0, real1, imag1, ...]. Last dimension must be an even number. The output data has shape: [N, d/2] or
[N_1, N_2, N_3, d/2]. It is only the real part of the result.
But OpenVINO DFT and IDFT operations uses complex input data represented as real tensors of the shape
[N_1, ..., N_r, 2]. Also, the result of OpenVINO DFT and IDFT operations is always complex but represented as
a real tensor of the shape [M_1, ..., M_r, 2]. If OpenVINO DFT or IDFT have no input signal_size, the output shape
and the input shape are the same.
Hence, to convert MxNet FFT to OpenVINO DFT, we need
1) to convert input data from the shape [N, d] or [N_1, N_2, N_3, d] to shape [N, d, 1] or [N_1, N_2, N_3, d, 1]
respectively;
2) to pad converted data using pads_begin = [0, 0, 0] and pads_end = [0, 0, 1] for MxNet FFT input shape [N, d], or
using pads_begin [0, 0, 0, 0, 0] and pads_end = [0, 0, 0, 0, 1] for MxNet FFT input shape [N_1, N_2, N_3, d],
with mode=constant;
3) to put padded data into DFT input 0, using (-1) in 'axes' input;
4) to reshape calculated DFT output to the shape [N, 2 * d] for for MxNet FFT input shape [N, d], or to the shape
[N_1, N_2, N_3, 2 * d]
Finally, to convert MxNet IFFT to OpenVINO IDFT, we need
1) to reshape input data from the shape [N, d] or [N_1, N_2, N_3, d] to shape [N, d // 2, 2] or
[N_1, N_2, N_3, d // 2, 2] respectively;
2) to put reshaped input data to the input 0 of IDFT, using (-1) in 'axes' input;
3) to get real parts using Split + Squeeze.
"""
enabled = True
def find_and_replace_pattern(self, graph: Graph):
for mx_fft in graph.get_op_nodes(op='MXFFT'):
if mx_fft.soft_get('is_inverse', False):
self.convert_ifft_to_dft(graph, mx_fft)
else:
self.convert_fft_to_dft(graph, mx_fft)
def convert_fft_to_dft(self, graph: Graph, mx_fft: Node):
mx_fft_name = mx_fft.soft_get('name', mx_fft.id)
unsqueeze_node = create_op_with_const_inputs(graph, Unsqueeze, {1: int64_array([-1])},
{'name': mx_fft_name + '/Unsqueeze'})
rank_node = Rank(graph, {'name': mx_fft_name + '/Rank'}).create_node()
mx_fft_connection = mx_fft.in_port(0).get_connection()
mx_fft_connection.set_destination(unsqueeze_node.in_port(0))
mx_fft_connection.get_source().connect(rank_node.in_port(0))
add_node = create_op_with_const_inputs(graph, Add, {1: int64_array(1)},
{'name': mx_fft_name + '/Add'}, rank_node)
broadcast_node1 = create_op_with_const_inputs(graph, Broadcast, {0: int64_array(0)},
{'name': mx_fft_name + '/Pad_broadcast'})
add_node.out_port(0).connect(broadcast_node1.in_port(1))
scatter_node = create_op_with_const_inputs(graph, ScatterUpdate,
{2: int64_array(1), 3: int64_array(0)},
{'name': mx_fft_name + '/ScatterUpdate'})
broadcast_node1.out_port(0).connect(scatter_node.in_port(0))
rank_node.out_port(0).connect(scatter_node.in_port(1))
pad_node = Pad(graph, {'name': mx_fft_name + '/Pad', 'mode': 'constant'}).create_node([unsqueeze_node,
broadcast_node1,
scatter_node])
dft_node = create_op_with_const_inputs(graph, DFT, {1: int64_array([-1])},
{'name': mx_fft_name + '/DFT', 'in_ports_count': 2},
pad_node)
sub_node = create_op_with_const_inputs(graph, Sub, {1: int64_array(1)}, {'name': mx_fft_name + '/Sub'})
rank_node.out_port(0).connect(sub_node.in_port(0))
broadcast_node2 = create_op_with_const_inputs(graph, Broadcast, {0: int64_array(0)},
{'name': mx_fft_name + '/Reshape_broadcast'})
sub_node.out_port(0).connect(broadcast_node2.in_port(1))
concat_node = create_op_with_const_inputs(graph, Concat, {1: int64_array([-1, 2])},
{'name': mx_fft_name + '/New_shape', 'in_ports_count': 2, 'axis': 0},
broadcast_node2)
reshape_node = Reshape(graph, {}).create_node([dft_node, concat_node])
mx_fft.out_port(0).get_connection().set_source(reshape_node.out_port(0))
rename_nodes([(mx_fft, mx_fft_name + '/to_be_removed'), (reshape_node, mx_fft_name)])
def convert_ifft_to_dft(self, graph: Graph, mx_fft: Node):
mx_fft_name = mx_fft.soft_get('name', mx_fft.id)
rank_node = Rank(graph, {'name': mx_fft_name + '/rank'}).create_node()
sub_node = create_op_with_const_inputs(graph, Sub, {1: int64_array(1)}, {'name': mx_fft_name + '/Sub'})
rank_node.out_port(0).connect(sub_node.in_port(0))
broadcast_node0 = create_op_with_const_inputs(graph, Broadcast, {0: int64_array(0)},
{'name': mx_fft_name + '/broadcast'})
sub_node.out_port(0).connect(broadcast_node0.in_port(1))
concat_node = create_op_with_const_inputs(graph, Concat, {1: int64_array([-1, 2])},
{'name': mx_fft_name + '/new_shape', 'in_ports_count': 2, 'axis': 0},
broadcast_node0)
reshape_node = Reshape(graph, {'name': mx_fft_name + '/reshape'}).create_node()
concat_node.out_port(0).connect(reshape_node.in_port(1))
mx_fft_connection = mx_fft.in_port(0).get_connection()
mx_fft_connection.set_destination(reshape_node.in_port(0))
mx_fft_connection.get_source().connect(rank_node.in_port(0))
dft_node = create_op_with_const_inputs(graph, IDFT, {1: int64_array([-1])},
{'name': mx_fft_name + '/idft', 'in_ports_count': 2},
reshape_node)
split_node = create_op_with_const_inputs(graph, Split, {1: int64_array(-1)},
{'name': mx_fft_name + '/split', 'num_splits': 2},
dft_node)
squeeze_node = create_op_with_const_inputs(graph, Squeeze, {1: int64_array([-1])}, {}, split_node)
mx_fft.out_port(0).get_connection().set_source(squeeze_node.out_port(0))
rename_nodes([(mx_fft, mx_fft_name + '/to_be_removed'), (squeeze_node, mx_fft_name)])
@@ -0,0 +1,25 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from extensions.ops.mxfft import MXFFT
from mo.front.extractor import FrontExtractorOp
class FFTFrontExtractor(FrontExtractorOp):
op = 'fft'
enabled = True
@classmethod
def extract(cls, node):
MXFFT.update_node_stat(node, {'is_inverse': False})
return cls.enabled
class IFFTFrontExtractor(FrontExtractorOp):
op = 'ifft'
enabled = True
@classmethod
def extract(cls, node):
MXFFT.update_node_stat(node, {'is_inverse': True})
return cls.enabled
@@ -0,0 +1,36 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from extensions.ops.elementwise import Pow
from extensions.ops.ReduceOps import ReduceSum
from mo.front.common.partial_infer.utils import int64_array
from mo.front.common.replacement import FrontReplacementSubgraph
from mo.front.tf.graph_utils import create_op_with_const_inputs
from mo.graph.graph import Graph, rename_nodes
from mo.middle.passes.convert_data_type import data_type_str_to_np
class ComplexAbs(FrontReplacementSubgraph):
enabled = True
def run_after(self):
from extensions.front.tf.ComplexAbsAfterComplex import ComplexAbsAfterComplex
return [ComplexAbsAfterComplex]
def find_and_replace_pattern(self, graph: Graph):
for complex_abs in graph.get_op_nodes(op='ComplexAbs'):
complex_abs_name = complex_abs.soft_get('name', complex_abs.id)
power_type = data_type_str_to_np(graph.graph['cmd_params'].data_type)
squared = create_op_with_const_inputs(graph, Pow, {1: power_type(2.0)},
{'name': complex_abs_name + '/squared_parts'})
complex_abs.in_port(0).get_connection().set_destination(squared.in_port(0))
sum = create_op_with_const_inputs(graph, ReduceSum, {1: int64_array(-1)},
{'name': complex_abs_name + '/squared_abs'},
squared)
sqrt = create_op_with_const_inputs(graph, Pow, {1: power_type(0.5)}, {}, sum)
complex_abs.out_port(0).get_connection().set_source(sqrt.out_port(0))
rename_nodes([(complex_abs, complex_abs_name + '/to_be_removed'), (sqrt, complex_abs_name)])
@@ -0,0 +1,69 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from extensions.ops.elementwise import Add, Pow
from mo.front.common.replacement import FrontReplacementSubgraph
from mo.front.subgraph_matcher import SubgraphMatch
from mo.front.tf.graph_utils import create_op_with_const_inputs
from mo.graph.graph import Graph, rename_nodes
from mo.middle.passes.convert_data_type import data_type_str_to_np
class ComplexAbsAfterComplex(FrontReplacementSubgraph):
"""
This transformation converts a sub-graph
SomeOp1 SomeOp2
| |
------------
|
Complex
|
ComplexAbs
into the sub-graph
SomeOp1 SomeOp2
| |
Constant[2]--Pow Pow--Constant[2]
| |
-------------
Add
|
Pow--Constant[0.5]
"""
enabled = True
def pattern(self):
return dict(
nodes=[
('complex', dict(op='Complex')),
('abs', dict(op='ComplexAbs')),
],
edges=[
('complex', 'abs', {'in': 0}),
])
def replace_sub_graph(self, graph: Graph, match: [dict, SubgraphMatch]):
cmp = match['complex']
complex_abs = match['abs']
complex_abs_name = complex_abs.soft_get('name', complex_abs.id)
power_type = data_type_str_to_np(graph.graph['cmd_params'].data_type)
pow0 = create_op_with_const_inputs(graph, Pow, {1: power_type(2.0)},
{'name': complex_abs_name + '/real_part_squared'})
pow1 = create_op_with_const_inputs(graph, Pow, {1: power_type(2.0)},
{'name': complex_abs_name + '/imag_part_squared'})
cmp.in_port(0).get_connection().set_destination(pow0.in_port(0))
cmp.in_port(1).get_connection().set_destination(pow1.in_port(0))
add = Add(graph, {'name': complex_abs_name + '/squared_abs'}).create_node([pow0, pow1])
sqrt = create_op_with_const_inputs(graph, Pow, {1: power_type(0.5)}, {})
add.out_port(0).connect(sqrt.in_port(0))
complex_abs.out_port(0).get_connection().set_source(sqrt.out_port(0))
rename_nodes([(complex_abs, complex_abs_name + '/to_be_removed'), (sqrt, complex_abs_name)])
@@ -0,0 +1,27 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from mo.front.common.partial_infer.utils import int64_array
from mo.front.common.replacement import FrontReplacementSubgraph
from mo.front.tf.graph_utils import add_constant_to_negative_values
from mo.graph.graph import Graph
class CorrectRollAxes(FrontReplacementSubgraph):
"""
The transformation SSliceComplex removes 2 StridedSlice and Complex operation. If the Roll node is a consumer
of Complex node in the original TF model, then we have a real input tensor for Roll instead of a complex.
Negative axes values for the Roll operation should be updated to reflect the fact that the rank of input tensor was
increased by one (a new trailing dimension of size 2 containing real and imaginary part of complex number is added).
"""
enabled = True
def run_after(self):
from extensions.front.tf.SSliceComplex import SSliceComplex
return [SSliceComplex]
def find_and_replace_pattern(self, graph: Graph):
for roll in graph.get_op_nodes(op='Roll', input_rank_changed=True):
add_constant_to_negative_values(roll, 2, int64_array(-1))
del roll['input_rank_changed']
@@ -765,6 +765,7 @@ class ObjectDetectionAPIPreprocessor2Replacement(FrontReplacementFromConfigFileG
else: # case 1
# change output of the end_node to be produced with the last preprocessing op
end_node.out_port(0).get_connection().set_source(pre_processing_ops[-1][0].out_port(0))
start_node.in_port(0).disconnect()
else: # simply remove the nodes in between start_node and end_node (including them). Case 3 and 6
end_node.out_port(0).get_connection().set_source(start_node.in_port(0).get_source())
@@ -0,0 +1,73 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from mo.front.common.partial_infer.utils import int64_array
from mo.front.common.replacement import FrontReplacementSubgraph
from mo.front.subgraph_matcher import SubgraphMatch
from mo.front.tf.graph_utils import add_constant_to_negative_values
from mo.graph.graph import Graph
class RollRealImagPack(FrontReplacementSubgraph):
"""
Some TF models contain Roll for complex data, as a part of the sub-graph
input shift axes
| | |
-------------------
Roll
|
-------------------
| |
Real Imag
| |
------- -------
| |
Pack
|
SomeOp
This sub-graph can be replaced with the sub-graph
input shift axes
| | |
-------------------
Roll
|
SomeOp
But after such replacement, we should correct axes of Roll, because input data are real now. Namely, if
there are negative axes for Roll, we need subtract 1 from such axes indices.
"""
enabled = True
def run_after(self):
from extensions.front.tf.SSliceComplex import SSliceComplex
return [SSliceComplex]
def run_before(self):
from extensions.front.Pack import Pack
return [Pack]
def pattern(self):
return dict(
nodes=[
('unroll', dict(op='Roll')),
('real', dict(op='Real')),
('imag', dict(op='Imag')),
('pack', dict(op='Pack')),
],
edges=[
('unroll', 'real', {'in': 0}),
('unroll', 'imag', {'in': 0}),
('real', 'pack', {'in': 0}),
('imag', 'pack', {'in': 1}),
])
def replace_sub_graph(self, graph: Graph, match: [dict, SubgraphMatch]):
unroll = match['unroll']
add_constant_to_negative_values(unroll, 2, int64_array(-1))
pack = match['pack']
pack.out_port(0).get_connection().set_source(unroll.out_port(0))
graph.remove_nodes_from([match['real'].id, match['imag'].id])
@@ -0,0 +1,70 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import logging as log
from mo.front.common.replacement import FrontReplacementSubgraph
from mo.front.subgraph_matcher import SubgraphMatch
from mo.graph.graph import Graph
class SSliceComplex(FrontReplacementSubgraph):
"""
Some TF models contain the sub-graph
SomeOp
|
--------------------------
| |
StridedSlice StridedSlice
| |
------------------------
Complex
|
| other inputs
| | ... |
-------------------
SomeOp1
Here SomeOp is some node with real output and with the shape [N_0, ..., N_{r - 1}, 2], and StridedSlice nodes
have output shapes [N_0, ..., N_{r - 1}].
But MO and Inference Engine do not support complex tensors. Hence, we need to replace this sub-graph with
SomeOp other inputs
| | ... |
-------------------
SomeOp1
After this transformation we need to mark SomeOp1 operation that its input rank has changed because
its inputs/attributes should probably be updated. Currently we have such a case for a Roll operation.
"""
enabled = True
def pattern(self):
return dict(
nodes=[
('strided_slice_real', dict(op='StridedSlice')),
('strided_slice_imag', dict(op='StridedSlice')),
('complex', dict(op='Complex')),
],
edges=[
('strided_slice_real', 'complex', {'in': 0}),
('strided_slice_imag', 'complex', {'in': 1}),
])
def replace_sub_graph(self, graph: Graph, match: [dict, SubgraphMatch]):
strided_slice_real = match['strided_slice_real']
strided_slice_imag = match['strided_slice_imag']
real_input = strided_slice_real.in_port(0).get_source().node
imag_input = strided_slice_imag.in_port(0).get_source().node
if real_input.id != imag_input.id:
log.debug('The pattern does not correspond to operation for complex tensor. Different inputs.')
return
complex_node = match['complex']
for dst in complex_node.out_port(0).get_connection().get_destinations():
after_complex_node = dst.node
after_complex_node['input_rank_changed'] = True
complex_node.out_port(0).get_connection().set_source(strided_slice_real.in_port(0).get_source())
@@ -0,0 +1,38 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from extensions.ops.dft import DFT, IDFT
from mo.front.common.partial_infer.utils import int64_array
from mo.front.common.replacement import FrontReplacementSubgraph
from mo.front.tf.graph_utils import create_op_with_const_inputs
from mo.graph.graph import Graph, rename_nodes
class TFFFTToDFT(FrontReplacementSubgraph):
"""
This transformation converts the operation TFFFT into OpenVINO DFT (if the attribute 'is_inverse' is False),
or into OpenVINO IDFT (otherwise).
"""
enabled = True
def run_after(self):
from extensions.front.tf.RollRealImagPack import RollRealImagPack
return [RollRealImagPack]
def find_and_replace_pattern(self, graph: Graph):
for tf_fft in graph.get_op_nodes(op='TFFFT'):
tf_fft_name = tf_fft.soft_get('name', tf_fft.id)
num_of_dims = tf_fft.soft_get('num_of_dimensions', 1)
axes = int64_array(range(-num_of_dims, 0))
op = IDFT if tf_fft.soft_get('is_inverse', False) else DFT
dft_node = create_op_with_const_inputs(graph, op, {1: axes}, {'in_ports_count': 2},
tf_fft.in_port(0).get_source().node)
tf_fft.out_port(0).get_connection().set_source(dft_node.out_port(0))
rename_nodes([(tf_fft, tf_fft_name + '/to_be_removed'), (dft_node, tf_fft_name)])
if graph.graph['layout'] == 'NHWC':
dft_node['need_insert_transposes_for_dft'] = True
@@ -0,0 +1,71 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from extensions.ops.TFFFT import TFFFT
from mo.front.extractor import FrontExtractorOp
class FFT1DOpFrontExtractor(FrontExtractorOp):
op = 'FFT'
enabled = True
@classmethod
def extract(cls, node):
attrs = {'num_of_dimensions': 1, 'is_inverse': False}
TFFFT.update_node_stat(node, attrs)
return cls.enabled
class FFT2DOpFrontExtractor(FrontExtractorOp):
op = 'FFT2D'
enabled = True
@classmethod
def extract(cls, node):
attrs = {'num_of_dimensions': 2, 'is_inverse': False}
TFFFT.update_node_stat(node, attrs)
return cls.enabled
class FFT3DOpFrontExtractor(FrontExtractorOp):
op = 'FFT3D'
enabled = True
@classmethod
def extract(cls, node):
attrs = {'num_of_dimensions': 3, 'is_inverse': False}
TFFFT.update_node_stat(node, attrs)
return cls.enabled
class IFFT1DOpFrontExtractor(FrontExtractorOp):
op = 'IFFT'
enabled = True
@classmethod
def extract(cls, node):
attrs = {'num_of_dimensions': 1, 'is_inverse': True}
TFFFT.update_node_stat(node, attrs)
return cls.enabled
class IFFT2DOpFrontExtractor(FrontExtractorOp):
op = 'IFFT2D'
enabled = True
@classmethod
def extract(cls, node):
attrs = {'num_of_dimensions': 2, 'is_inverse': True}
TFFFT.update_node_stat(node, attrs)
return cls.enabled
class IFFT3DOpFrontExtractor(FrontExtractorOp):
op = 'IFFT3D'
enabled = True
@classmethod
def extract(cls, node):
attrs = {'num_of_dimensions': 3, 'is_inverse': True}
TFFFT.update_node_stat(node, attrs)
return cls.enabled
@@ -1,10 +1,12 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import numpy as np
from extensions.middle.pass_separator import PostMiddleStart
from extensions.ops.transpose import Transpose
from mo.graph.graph import Graph, Node
from mo.front.common.partial_infer.utils import int64_array
from mo.graph.graph import Graph, Node, Port
from mo.middle.replacement import MiddleReplacementPattern
from mo.ops.op import PermuteAttrs
@@ -159,3 +161,28 @@ def mark_as_correct_data_layout(node: Node):
for ind, port in node.out_ports().items():
mark_output_as_in_correct_layout(node, ind)
def insert_transpose(graph: Graph, input_port: Port, before_input=True):
from mo.front.tf.graph_utils import create_op_with_const_inputs
input_rank = len(input_port.data.get_shape())
if input_rank > 3:
if before_input:
axis_order = np.concatenate((int64_array([0]),
int64_array(list(range(2, input_rank))),
int64_array([1])))
source_node = input_port.get_source().node
transpose_name = source_node.soft_get('name', source_node.id) + '/TransposeToNHWC'
else:
axis_order = np.concatenate(
(int64_array([0]),
int64_array([input_rank - 1]),
int64_array(list(range(1, input_rank - 1)))))
transpose_name = input_port.node.soft_get('name', input_port.node.id) + '/TransposeToNCHW'
input_port.node['need_shape_inference'] = True
input_port.node['override_output_shape'] = True
transpose = create_op_with_const_inputs(graph, Transpose, {1: axis_order}, {'name': transpose_name})
input_port.get_connection().insert_node(transpose)
transpose['need_shape_inference'] = True
transpose['override_output_shape'] = True
+31
View File
@@ -0,0 +1,31 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from mo.graph.graph import Graph
from mo.ops.op import Op
class TFFFT(Op):
"""
This operation is intended to read TF operations FFT, FFT2D, FFT3D, IFFT, IFFT2D, IFFT3D.
The operation TFFFT has two attributes: an integer attribute num_of_dimensions and a boolean attribute is_inverse.
If an operation to read is FFT, FFT2D, or FFT3D, then the attribute 'is_inverse' is False, and True otherwise.
The attribute 'num_of_dimensions' is equal to number of transformed axes, i.e. 1 for FFT and IFFT, 2 for FFT2D and
IFFT2D, 3 for FFT3D and IFFT3D.
The transformation TFFFTToDFT converts the operation TFFFT into MO DFT (if the attribute 'is_inverse' is False),
or into MO IDFT (otherwise).
"""
op = 'TFFFT'
enabled = False
def __init__(self, graph: Graph, attrs: dict):
mandatory_props = {
'op': self.op,
'out_ports_count': 1,
'in_ports_count': 1,
}
assert 'is_inverse' in attrs, 'Attribute is_inverse is not given for the operation TFFFT.'
assert 'num_of_dimensions' in attrs, 'Attribute num_of_dimensions is not given for the operation TFFFT.'
super().__init__(graph, mandatory_props, attrs)
+127
View File
@@ -0,0 +1,127 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from mo.front.common.partial_infer.utils import int64_array
from mo.graph.graph import Node, Graph
from mo.ops.op import Op
class FFTBase(Op):
enabled = False
op = None
version = 'opset7'
def __init__(self, graph: Graph, attrs: dict):
mandatory_props = {
'out_ports_count': 1,
'in_ports_count': 3,
'version': self.version,
'infer': self.infer
}
super().__init__(graph, mandatory_props, attrs)
def infer(self, node: Node):
node_name = node.soft_get(node.name, node.id)
assert len([p for p in node.in_ports().values() if not p.disconnected()]) in [2, 3], \
'(I)DFT node {} must have 2 or 3 inputs'.format(node_name)
src_shape = node.in_port(0).data.get_shape()
assert src_shape is not None, 'The input data shape of (I)DFT node {} must not be None'.format(node_name)
assert src_shape[-1] == 2, \
'The last dimension of input shape of (I)DFT node {} should be equal to 2'.format(node_name)
input_rank = len(src_shape)
assert input_rank >= 2, 'The input rank of (I)DFT node {} should be greater or equal to 2'.format(node_name)
axes = FFTBase.get_axes(node)
assert input_rank >= len(axes) + 1, \
'The input rank must be greater than number of (I)DFT node {} axes'.format(node_name)
axes = FFTBase.canonicalize_axes(axes, input_rank)
assert (input_rank - 1) not in axes, '(I)DFT node {} axes cannot contain the last axis'.format(node_name)
assert len(set(axes)) == len(axes), '(I)DFT node {} axes must be unique.'.format(node_name)
output_shape = int64_array(src_shape)
if node.is_in_port_connected(2):
signal_size = FFTBase.get_signal_size(node)
signal_size = FFTBase.canonicalize_signal_size(signal_size, axes, src_shape)
output_shape[axes] = signal_size
node.out_port(0).data.set_shape(output_shape)
@staticmethod
def canonicalize_axes(axes, input_rank):
"""
FFT operation supports for negative axes to transform. More precisely, according to the FFT operation
specification, axes should be integers from -(r - 1) to (r - 2) inclusively, where r = rank(data).
A negative axis 'a' is interpreted as an axis 'r - 1 + a'. The reason is the following: real input
tensor of the shape [n_0, ..., n_{r - 1}, 2] is interpreted as a complex tensor with the shape
[n_0, ..., n_{r - 1}]. Hence, we need to 'canonicalize' axes using the formula 'r - 1 + a'.
:param axes: axes to canonicalize
:param input_rank: input tensor rank
:return: canonicalized axes
"""
result = axes.copy()
for i, axis in enumerate(axes):
if axis < 0:
result[i] = axis + input_rank - 1
return result
@staticmethod
def canonicalize_signal_size(signal_size, axes, input_shape):
result = signal_size.copy()
for i, axis in enumerate(axes):
size = signal_size[i]
if size == -1:
result[i] = input_shape[axis]
return result
@staticmethod
def get_axes(node: Node):
axes = node.in_port(1).get_source().data.get_value()
node_name = node.soft_get('name', node.id)
assert axes is not None, 'The input with axes is not constant for node {}'.format(node_name)
return int64_array(axes)
@staticmethod
def get_signal_size(node: Node):
src_shape = node.in_port(0).data.get_shape()
assert src_shape is not None
input_rank = len(src_shape)
if node.is_in_port_connected(2):
signal_size = node.in_port(2).get_source().data.get_value()
else:
axes = FFTBase.get_axes(node)
signal_size = [src_shape[: input_rank - 1][a] for a in axes]
node_name = node.soft_get('name', node.id)
assert signal_size is not None, 'The input with signal_size is not constant for node {}'.format(node_name)
return int64_array(signal_size)
class DFT(FFTBase):
op = 'DFT'
enabled = False
def __init__(self, graph: Graph, attrs: dict):
mandatory_props = {
'type': self.op,
'op': self.op,
}
mandatory_props.update(attrs)
super().__init__(graph, mandatory_props)
class IDFT(FFTBase):
op = 'IDFT'
enabled = False
def __init__(self, graph: Graph, attrs: dict):
mandatory_props = {
'type': self.op,
'op': self.op,
}
mandatory_props.update(attrs)
super().__init__(graph, mandatory_props)
+42
View File
@@ -0,0 +1,42 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from mo.front.common.partial_infer.utils import int64_array
from mo.graph.graph import Graph, Node
from mo.ops.op import Op
class MXFFT(Op):
"""
This operation is intended to read MxNet operations FFT and IFFT.
The operation MXFFT has one attribute: a boolean attribute is_inverse.
If an operation to read is FFT, then the attribute 'is_inverse' is False, and True otherwise.
The transformation MXFFTToDFT converts the operation MXFFT into MO DFT (if the attribute 'is_inverse'
is False), or into MO IDFT (otherwise).
"""
op = 'MXFFT'
enabled = False
def __init__(self, graph: Graph, attrs: dict):
mandatory_props = {
'op': self.op,
'out_ports_count': 1,
'in_ports_count': 1,
'infer': self.infer
}
assert 'is_inverse' in attrs, 'Attribute is_inverse is not given for the operation MXFFT.'
super().__init__(graph, mandatory_props, attrs)
def infer(self, node: Node):
node_name = node.soft_get('name', node.id)
input_shape = node.in_port(0).data.get_shape()
assert input_shape is not None, 'Input shape of MXFFT node {} must not be None'.format(node_name)
is_inverse = node.soft_get('is_inverse', False)
output_shape = input_shape.copy()
if is_inverse:
output_shape[-1] = output_shape[-1] // 2
else:
output_shape[-1] = output_shape[-1] * 2
node.out_port(0).data.set_shape(int64_array(output_shape))
+2 -2
View File
@@ -8,7 +8,8 @@ from mo.ops.op import Op
class Roll(Op):
""" Roll operation that shifts elements of a tensor along specified axes.
"""
Roll operation that shifts elements of a tensor along specified axes.
"""
op = 'Roll'
enabled = False
@@ -26,7 +27,6 @@ class Roll(Op):
class AttributedRoll(Op):
""" Roll operation that shifts elements of a tensor along specified axes.
This operation uses the same semantics as Roll but with shift and axes specified as attributes.
Shift and axes are specified as attributes in MxNet.
"""
@@ -10,6 +10,7 @@ import numpy as np
from extensions.middle.InsertLayoutPropagationTransposes import mark_input_as_in_correct_layout, \
mark_output_as_in_correct_layout
from extensions.ops.activation_ops import Sigmoid
from extensions.ops.elementwise import Add, Less, Mul
from mo.front.common.partial_infer.utils import int64_array
from mo.graph.graph import Node, Graph
from mo.ops.concat import Concat
@@ -185,3 +186,31 @@ def add_activation_function_after_node(graph: Graph, node: Node, activation_func
else:
raise Error('Unknown post-processing activation function "{}".'.format(activation_function))
return activation_node
def add_constant_to_negative_values(node: Node, port_idx: int, added_value: np.array):
"""
This function adds the given values to negative elements of value from the given input port.
:param node: node with corrected values in the input port port_idx
:param port_idx: input port index for negative values
:param added_value: the value to add
:return: None
"""
negative_values_source = node.in_port(port_idx).get_source()
negative_values_node = node.in_port(port_idx).get_source().node
negative_values_node_name = negative_values_node.soft_get('name', negative_values_node.id)
graph = node.graph
less_node = create_op_with_const_inputs(graph, Less,
{1: np.array(0, dtype=added_value.dtype)},
{'name': negative_values_node_name + '/Less'})
mul_node = create_op_with_const_inputs(graph, Mul, {1: added_value}, {'name': negative_values_node_name + '/Mul'})
node.in_port(port_idx).get_connection().set_destination(less_node.in_port(0))
less_node.out_port(0).connect(mul_node.in_port(0))
add_node = Add(graph, {}).create_node()
mul_node.out_port(0).connect(add_node.in_port(1))
negative_values_source.connect(add_node.in_port(0))
add_node.out_port(0).connect(node.in_port(port_idx))
@@ -133,12 +133,18 @@ def compare_graphs(graph: Graph, graph_ref: Graph, last_node: str, last_node_ref
if in_node.id not in checked_nodes_ref and in_node.id not in q_ref:
q_ref.append(in_node.id)
out_nodes = node.out_nodes().values() if node.kind == 'op' else sorted_by_name(node.out_nodes())
if node.kind == 'op':
out_nodes = sorted_by_name([Node(graph, v) for v, _ in node.get_outputs()])
else:
out_nodes = sorted_by_name(node.out_nodes())
for out_node in out_nodes:
if out_node.id not in checked_nodes and out_node.id not in q:
q.append(out_node.id)
out_nodes = node_ref.out_nodes().values() if node_ref.kind == 'op' else sorted_by_name(node_ref.out_nodes())
if node_ref.kind == 'op':
out_nodes = sorted_by_name([Node(graph_ref, v) for v, _ in node_ref.get_outputs()])
else:
out_nodes = sorted_by_name(node_ref.out_nodes())
for out_node in out_nodes:
if out_node.id not in checked_nodes_ref and out_node.id not in q_ref:
q_ref.append(out_node.id)
@@ -11,6 +11,7 @@ from extensions.middle.FakeSplitOutputs import AddFakeOutputsToSplit
from extensions.ops.Cast import Cast
from extensions.ops.ReduceOps import ReduceOp
from extensions.ops.activation_ops import Activation
from extensions.ops.dft import FFTBase
from extensions.ops.elementwise import Elementwise, UnaryElementwise, LogicalElementwise, BiasAdd, Div, Mul, Pow, Sub
from extensions.ops.embedding_bag import EmbeddingBagBase
from extensions.ops.loop import Loop
@@ -60,7 +61,7 @@ def collect_ops(path: str):
import_by_path(os.path.join(path, 'mo', 'ops'), ['mo', 'ops'])
import_by_path(os.path.join(path, 'extensions', 'ops'), ['extensions', 'ops'])
update_registration(classes=[Op, Activation, Elementwise, UnaryElementwise, LogicalElementwise,
EmbeddingBagBase, ReduceOp, Scatter, ScatterNDBase],
EmbeddingBagBase, ReduceOp, Scatter, ScatterNDBase, FFTBase],
enabled_transforms=[], disabled_transforms=[])
@@ -0,0 +1,84 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
from extensions.back.TransposeDFT import TransposeDFT
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, \
regular_op_with_empty_data
dft_graph_node_attrs = {
**regular_op_with_shaped_data('placeholder', [8, 2, 40, 56], {'type': 'Parameter', 'op': 'Parameter'}),
**valued_const_with_data('axes', int64_array([-2, -1])),
**regular_op_with_shaped_data('dft', [8, 2, 40, 56], {'op': 'DFT', 'need_insert_transposes_for_dft': True}),
**regular_op_with_shaped_data('abs', [8, 2, 40, 56], {'type': 'Abs', 'op': 'Abs'}),
**result(),
}
dft_graph_edges = [
*connect('placeholder', '0:dft'),
*connect('axes', '1:dft'),
*connect('dft', 'abs'),
*connect('abs', 'output'),
]
transposed_dft_graph_node_attrs = {
**regular_op_with_shaped_data('placeholder', [8, 2, 40, 56], {'type': 'Parameter', 'op': 'Parameter'}),
**regular_op_with_empty_data('transpose_before',
{'type': 'Transpose', 'op': 'Transpose', 'need_shape_inference': True}),
**valued_const_with_data('transpose_before_axis_const', int64_array([0, 2, 3, 1])),
**regular_op_with_empty_data('transpose_after',
{'type': 'Transpose', 'op': 'Transpose', 'need_shape_inference': True}),
**valued_const_with_data('transpose_after_axis_const', int64_array([0, 3, 1, 2])),
**valued_const_with_data('dft_axes', int64_array([-2, -1])),
**regular_op_with_shaped_data('dft', [8, 2, 40, 56], {'op': 'DFT', 'need_insert_transposes_for_dft': True}),
**regular_op_with_shaped_data('abs', [8, 2, 40, 56], {'type': 'Abs', 'op': 'Abs'}),
**result(),
}
transposed_dft_graph_edges = [
*connect('placeholder', '0:transpose_before'),
*connect('transpose_before_axis_const', '1:transpose_before'),
*connect('transpose_before', '0:dft'),
*connect('dft_axes', '1:dft'),
*connect('dft', '0:transpose_after'),
*connect('transpose_after_axis_const', '1:transpose_after'),
*connect('transpose_after', 'abs'),
*connect('abs', 'output'),
]
nontransposed_dft_graph_node_attrs = {
**regular_op_with_shaped_data('placeholder', [8, 2, 40, 56], {'type': 'Parameter', 'op': 'Parameter'}),
**valued_const_with_data('axes', int64_array([-2, -1])),
**regular_op_with_shaped_data('dft', [8, 2, 40, 56], {'op': 'DFT'}),
**regular_op_with_shaped_data('abs', [8, 2, 40, 56], {'type': 'Abs', 'op': 'Abs'}),
**result(),
}
nontransposed_dft_graph_edges = [
*connect('placeholder', '0:dft'),
*connect('axes', '1:dft'),
*connect('dft', 'abs'),
*connect('abs', 'output'),
]
class TransposeDFTTest(unittest.TestCase):
def test_dft_transpose(self):
graph = build_graph(nodes_attrs=dft_graph_node_attrs, edges=dft_graph_edges)
ref_graph = build_graph(nodes_attrs=transposed_dft_graph_node_attrs, edges=transposed_dft_graph_edges)
graph.graph['fw'] = 'tf'
TransposeDFT().find_and_replace_pattern(graph)
(flag, resp) = compare_graphs(graph, ref_graph, 'output')
self.assertTrue(flag, resp)
def test_dft_nontranspose(self):
graph = build_graph(nodes_attrs=nontransposed_dft_graph_node_attrs, edges=nontransposed_dft_graph_edges)
ref_graph = build_graph(nodes_attrs=nontransposed_dft_graph_node_attrs, edges=nontransposed_dft_graph_edges)
TransposeDFT().find_and_replace_pattern(graph)
(flag, resp) = compare_graphs(graph, ref_graph, 'output')
self.assertTrue(flag, resp)
@@ -0,0 +1,191 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
from generator import generator, generate
from extensions.front.mxnet.MXFFTToDFT import MXFFTToDFT
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
fft_graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'fft': {'kind': 'op', 'op': 'MXFFT', 'is_inverse': False},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
fft_graph_edges = [
('placeholder', 'fft', {'in': 0}),
('fft', 'abs'),
('abs', 'output'),
]
ref_converted_fft_graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'rank': {'kind': 'op', 'op': 'Rank'},
'unsqueeze': {'type': 'Unsqueeze', 'kind': 'op', 'op': 'Unsqueeze'},
'unsqueeze_axis': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([1]), 'value': int64_array([-1])
},
'one': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(1)
},
'add': {'type': 'Add', 'kind': 'op', 'op': 'Add'},
'zero1': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(0)
},
'broadcast1': {'type': 'Broadcast', 'kind': 'op', 'op': 'Broadcast'},
'one2': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(1)
},
'zero2': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(0)
},
'scatter': {'type': 'ScatterUpdate', 'kind': 'op', 'op': 'ScatterUpdate'},
'pad': {'type': 'Pad', 'kind': 'op', 'op': 'Pad', 'mode': 'constant'},
'fft_axes': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([1]), 'value': int64_array([-1])
},
'fft': {'kind': 'op', 'op': 'DFT', 'type': 'DFT'},
'one3': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(1)
},
'sub': {'type': 'Subtract', 'kind': 'op', 'op': 'Sub'},
'zero3': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(0)
},
'broadcast2': {'type': 'Broadcast', 'kind': 'op', 'op': 'Broadcast'},
'm1_2': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([-1, 2])
},
'concat': {'type': 'Concat', 'kind': 'op', 'op': 'Concat', 'axis': 0},
'reshape': {'kind': 'op', 'op': 'Reshape', 'type': 'Reshape'},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
ref_converted_fft_graph_edges = [
('placeholder', 'rank', {'in': 0, 'out': 0}),
('placeholder', 'unsqueeze', {'in': 0, 'out': 0}),
('unsqueeze_axis', 'unsqueeze', {'in': 1, 'out': 0}),
('rank', 'add', {'in': 0, 'out': 0}),
('one', 'add', {'in': 1, 'out': 0}),
('zero1', 'broadcast1', {'in': 0, 'out': 0}),
('add', 'broadcast1', {'in': 1, 'out': 0}),
('broadcast1', 'scatter', {'in': 0, 'out': 0}),
('rank', 'scatter', {'in': 1, 'out': 0}),
('one2', 'scatter', {'in': 2, 'out': 0}),
('zero2', 'scatter', {'in': 3, 'out': 0}),
('unsqueeze', 'pad', {'in': 0, 'out': 0}),
('broadcast1', 'pad', {'in': 1, 'out': 0}),
('scatter', 'pad', {'in': 2, 'out': 0}),
('pad', 'fft', {'in': 0, 'out': 0}),
('fft_axes', 'fft', {'in': 1, 'out': 0}),
('rank', 'sub', {'in': 0, 'out': 0}),
('one3', 'sub', {'in': 1, 'out': 0}),
('zero3', 'broadcast2', {'in': 0, 'out': 0}),
('sub', 'broadcast2', {'in': 1, 'out': 0}),
('broadcast2', 'concat', {'in': 0, 'out': 0}),
('m1_2', 'concat', {'in': 1, 'out': 0}),
('fft', 'reshape', {'in': 0, 'out': 0}),
('concat', 'reshape', {'in': 1, 'out': 0}),
('reshape', 'abs'),
('abs', 'output'),
]
ref_converted_ifft_graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'rank': {'kind': 'op', 'op': 'Rank'},
'subtracted_one': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(1)
},
'sub': {'type': 'Subtract', 'kind': 'op', 'op': 'Sub'},
'broadcast': {'type': 'Broadcast', 'kind': 'op', 'op': 'Broadcast'},
'broadcasted_value': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(0)
},
'new_shape': {'type': 'Concat', 'kind': 'op', 'op': 'Concat', 'axis': 0},
'new_shape_const': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([-1, 2])
},
'reshape': {'kind': 'op', 'op': 'Reshape', 'type': 'Reshape'},
'fft': {'kind': 'op', 'op': 'IDFT', 'type': 'IDFT'},
'fft_axes': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([1]), 'value': int64_array([-1])
},
'split': {'kind': 'op', 'op': 'Split', 'type': 'Split', 'num_splits': 2},
'split_axes': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(-1)
},
'squeeze': {'kind': 'op', 'op': 'Squeeze', 'type': 'Squeeze'},
'squeeze_axes': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([1]), 'value': int64_array([-1])
},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
ref_converted_ifft_graph_edges = [
('placeholder', 'rank', {'out': 0}),
('placeholder', 'reshape', {'out': 0}),
('rank', 'sub'),
('subtracted_one', 'sub'),
('broadcasted_value', 'broadcast'),
('sub', 'broadcast'),
('broadcast', 'new_shape'),
('new_shape_const', 'new_shape'),
('new_shape', 'reshape'),
('reshape', 'fft'),
('fft_axes', 'fft'),
('fft', 'split'),
('split_axes', 'split'),
('split', 'squeeze', {'out': 0}),
('squeeze_axes', 'squeeze'),
('squeeze', 'abs'),
('abs', 'output'),
]
@generator
class MXFFTToDFTTest(unittest.TestCase):
@generate(*[int64_array([3, 100, 100, 8]), int64_array([5, 60])])
def test_fft_replacement(self, input_shape):
graph = build_graph(nodes_attrs=fft_graph_node_attrs,
edges=fft_graph_edges,
update_attributes={
'placeholder': {'shape': input_shape}
})
graph.stage = 'front'
MXFFTToDFT().find_and_replace_pattern(graph)
ref_graph = build_graph(nodes_attrs=ref_converted_fft_graph_node_attrs,
edges=ref_converted_fft_graph_edges,
update_attributes={
'placeholder': {'shape': input_shape}
})
(flag, resp) = compare_graphs(graph, ref_graph, 'output')
self.assertTrue(flag, resp)
@generate(*[int64_array([3, 100, 100, 8]), int64_array([5, 60])])
def test_ifft_replacement(self, input_shape):
graph = build_graph(nodes_attrs=fft_graph_node_attrs,
edges=fft_graph_edges,
update_attributes={
'placeholder': {'shape': input_shape},
'fft': {'is_inverse': True}
})
graph.stage = 'front'
MXFFTToDFT().find_and_replace_pattern(graph)
ref_graph = build_graph(nodes_attrs=ref_converted_ifft_graph_node_attrs,
edges=ref_converted_ifft_graph_edges,
update_attributes={
'placeholder': {'shape': input_shape}
})
(flag, resp) = compare_graphs(graph, ref_graph, 'output')
self.assertTrue(flag, resp)
@@ -0,0 +1,74 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
import numpy as np
from extensions.front.tf.ComplexAbsAfterComplex import ComplexAbsAfterComplex
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
graph_node_attrs = {
'placeholder_0': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'placeholder_1': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'complex': {'kind': 'op', 'op': 'Complex'},
'complex_abs': {'kind': 'op', 'op': 'ComplexAbs'},
'relu': {'type': 'ReLU', 'kind': 'op', 'op': 'ReLU'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
graph_edges = [
('placeholder_0', 'complex', {'in': 0}),
('placeholder_1', 'complex', {'in': 1}),
('complex', 'complex_abs', {'in': 0}),
('complex_abs', 'relu'),
('relu', 'output'),
]
ref_graph_node_attrs = {
'placeholder_0': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'placeholder_1': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'pow0_const': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'value': np.float32(2.0)
},
'pow1_const': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'value': np.float32(2.0)
},
'pow0': {'type': 'Power', 'kind': 'op', 'op': 'Pow'},
'pow1': {'type': 'Power', 'kind': 'op', 'op': 'Pow'},
'add': {'type': 'Add', 'kind': 'op', 'op': 'Add'},
'sqrt_const': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'value': np.float32(0.5)
},
'sqrt': {'type': 'Power', 'kind': 'op', 'op': 'Pow'},
'relu': {'type': 'ReLU', 'kind': 'op', 'op': 'ReLU'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
ref_graph_edges = [
('placeholder_0', 'pow0', {'in': 0}),
('placeholder_1', 'pow1', {'in': 0}),
('pow0_const', 'pow0', {'in': 1}),
('pow1_const', 'pow1', {'in': 1}),
('pow0', 'add', {'in': 0}),
('pow1', 'add', {'in': 1}),
('add', 'sqrt', {'in': 0}),
('sqrt_const', 'sqrt', {'in': 1}),
('sqrt', 'relu'),
('relu', 'output'),
]
class ComplexAbsAfterComplexTest(unittest.TestCase):
def test_replacement(self):
graph = build_graph(nodes_attrs=graph_node_attrs, edges=graph_edges)
graph.stage = 'front'
ComplexAbsAfterComplex().find_and_replace_pattern(graph)
ref_graph = build_graph(nodes_attrs=ref_graph_node_attrs, edges=ref_graph_edges)
(flag, resp) = compare_graphs(graph, ref_graph, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)
@@ -0,0 +1,66 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
import numpy as np
from extensions.front.tf.ComplexAbs import ComplexAbs
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
graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'complex_abs': {'kind': 'op', 'op': 'ComplexAbs'},
'relu': {'type': 'ReLU', 'kind': 'op', 'op': 'ReLU'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
graph_edges = [
('placeholder', 'complex_abs'),
('complex_abs', 'relu'),
('relu', 'output'),
]
ref_graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'pow2_const': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'value': np.float32(2.0)
},
'pow2': {'type': 'Power', 'kind': 'op', 'op': 'Pow'},
'sum_axis': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'value': int64_array(-1)
},
'sum': {'type': 'ReduceSum', 'kind': 'op', 'op': 'ReduceSum'},
'sqrt_const': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'value': np.float32(0.5)
},
'sqrt': {'type': 'Power', 'kind': 'op', 'op': 'Pow'},
'relu': {'type': 'ReLU', 'kind': 'op', 'op': 'ReLU'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
ref_graph_edges = [
('placeholder', 'pow2', {'in': 0}),
('pow2_const', 'pow2', {'in': 1}),
('sum_axis', 'sum', {'in': 1}),
('pow2', 'sum', {'in': 0}),
('sum', 'sqrt', {'in': 0}),
('sqrt_const', 'sqrt', {'in': 1}),
('sqrt', 'relu'),
('relu', 'output'),
]
class ComplexAbsTest(unittest.TestCase):
def test_replacement(self):
graph = build_graph(nodes_attrs=graph_node_attrs, edges=graph_edges)
graph.stage = 'front'
ComplexAbs().find_and_replace_pattern(graph)
ref_graph = build_graph(nodes_attrs=ref_graph_node_attrs, edges=ref_graph_edges)
(flag, resp) = compare_graphs(graph, ref_graph, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)
@@ -0,0 +1,89 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
from extensions.front.tf.CorrectRollAxes import CorrectRollAxes
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
graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'roll': {'kind': 'op', 'op': 'Roll', 'type': 'Roll', 'input_rank_changed': True},
'roll_shift': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([50, 50])
},
'roll_axes': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([-2, -1])
},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
graph_edges = [
('placeholder', 'roll', {'in': 0}),
('roll', 'abs'),
('abs', 'output'),
('roll_shift', 'roll', {'in': 1}),
('roll_axes', 'roll', {'in': 2}),
]
ref_graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'roll': {'kind': 'op', 'op': 'Roll', 'type': 'Roll'},
'roll_shift': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([50, 50])
},
'roll_axes': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([-2, -1])
},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
'add': {'type': 'Add', 'kind': 'op', 'op': 'Add'},
'mul': {'type': 'Multiply', 'kind': 'op', 'op': 'Mul'},
'less': {'type': 'Less', 'kind': 'op', 'op': 'Less'},
'zero': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(0)
},
'minus_one': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(-1)
},
}
ref_graph_edges = [
('placeholder', 'roll', {'out': 0, 'in': 0}),
('roll', 'abs'),
('abs', 'output'),
('roll_shift', 'roll', {'in': 1}),
('mul', 'add', {'in': 1}),
('add', 'roll', {'in': 2}),
('zero', 'less', {'in': 1}),
('minus_one', 'mul', {'in': 1}),
('less', 'mul', {'in': 0}),
('roll_axes', 'less', {'out': 0, 'in': 0}),
('roll_axes', 'add', {'out': 0, 'in': 0}),
]
class CorrectRollAxesTest(unittest.TestCase):
def test_replacement(self):
graph = build_graph(nodes_attrs=graph_node_attrs, edges=graph_edges)
graph.stage = 'front'
CorrectRollAxes().find_and_replace_pattern(graph)
ref_graph = build_graph(nodes_attrs=ref_graph_node_attrs, edges=ref_graph_edges)
(flag, resp) = compare_graphs(graph, ref_graph, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)
def test_nonreplacement(self):
graph = build_graph(nodes_attrs=graph_node_attrs, edges=graph_edges,
update_attributes={'roll': {'input_rank_changed': False}})
graph.stage = 'front'
CorrectRollAxes().find_and_replace_pattern(graph)
ref_graph = build_graph(nodes_attrs=graph_node_attrs, edges=graph_edges,
update_attributes={'roll': {'input_rank_changed': False}})
(flag, resp) = compare_graphs(graph, ref_graph, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)
@@ -0,0 +1,88 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
from extensions.front.tf.RollRealImagPack import RollRealImagPack
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
graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'unroll': {'kind': 'op', 'op': 'Roll', 'type': 'Roll'},
'real': {'kind': 'op', 'op': 'Real'},
'imag': {'kind': 'op', 'op': 'Imag'},
'pack': {'kind': 'op', 'op': 'Pack'},
'unroll_shift': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([50, 50])
},
'unroll_axes': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([-2, -1])
},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
graph_edges = [
('placeholder', 'unroll', {'in': 0}),
('unroll', 'real', {'out': 0, 'in': 0}),
('unroll', 'imag', {'out': 0, 'in': 0}),
('real', 'pack', {'in': 0}),
('imag', 'pack', {'in': 1}),
('pack', 'abs'),
('abs', 'output'),
('unroll_shift', 'unroll', {'in': 1}),
('unroll_axes', 'unroll', {'in': 2}),
]
ref_graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'unroll': {'kind': 'op', 'op': 'Roll', 'type': 'Roll'},
'unroll_shift': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([50, 50])
},
'unroll_axes': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([-2, -1])
},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
'add': {'type': 'Add', 'kind': 'op', 'op': 'Add'},
'mul': {'type': 'Multiply', 'kind': 'op', 'op': 'Mul'},
'less': {'type': 'Less', 'kind': 'op', 'op': 'Less'},
'zero': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(0)
},
'minus_one': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([]), 'value': int64_array(-1)
},
}
ref_graph_edges = [
('placeholder', 'unroll', {'out': 0, 'in': 0}),
('unroll', 'abs'),
('abs', 'output'),
('unroll_shift', 'unroll', {'in': 1}),
('unroll_axes', 'unroll', {'in': 2}),
('mul', 'add', {'in': 1}),
('add', 'unroll', {'in': 2}),
('zero', 'less', {'in': 1}),
('minus_one', 'mul', {'in': 1}),
('less', 'mul', {'in': 0}),
('unroll_axes', 'less', {'out': 0, 'in': 0}),
('unroll_axes', 'add', {'out': 0, 'in': 0}),
]
class RollRealImagPackTest(unittest.TestCase):
def test_replacement(self):
graph = build_graph(nodes_attrs=graph_node_attrs, edges=graph_edges)
graph.stage = 'front'
RollRealImagPack().find_and_replace_pattern(graph)
ref_graph = build_graph(nodes_attrs=ref_graph_node_attrs, edges=ref_graph_edges)
(flag, resp) = compare_graphs(graph, ref_graph, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)
@@ -0,0 +1,143 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
from extensions.front.tf.SSliceComplex import SSliceComplex
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
graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'strided_slice_real': {
'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice', 'begin_mask': int64_array([1]),
'end_mask': int64_array([1]), 'ellipsis_mask': int64_array([1]), 'new_axis_mask': int64_array([0]),
'shrink_axis_mask': int64_array([0, 1]),
},
'strided_slice_imag': {
'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice', 'begin_mask': int64_array([1]),
'end_mask': int64_array([1]), 'ellipsis_mask': int64_array([1]), 'new_axis_mask': int64_array([0]),
'shrink_axis_mask': int64_array([0, 1]),
},
'complex': {'kind': 'op', 'op': 'Complex'},
'real_begin': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([0, 0])
},
'imag_begin': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([0, 1])
},
'real_end': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([0, 1])
},
'imag_end': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([0, 2])
},
'real_strides': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([1, 1])
},
'imag_strides': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([1, 1])
},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
graph_edges = [
('placeholder', 'strided_slice_real', {'out': 0, 'in': 0}),
('placeholder', 'strided_slice_imag', {'out': 0, 'in': 0}),
('strided_slice_real', 'complex', {'in': 0}),
('strided_slice_imag', 'complex', {'in': 1}),
('complex', 'abs'),
('abs', 'output'),
('real_begin', 'strided_slice_real', {'in': 1}),
('imag_begin', 'strided_slice_imag', {'in': 1}),
('real_end', 'strided_slice_real', {'in': 2}),
('imag_end', 'strided_slice_imag', {'in': 2}),
('real_strides', 'strided_slice_real', {'in': 3}),
('imag_strides', 'strided_slice_imag', {'in': 3}),
]
ref_graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
ref_graph_edges = [
('placeholder', 'abs'),
('abs', 'output'),
]
non_transformed_graph_node_attrs = {
'placeholder_0': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'placeholder_1': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'strided_slice_real': {
'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice', 'begin_mask': int64_array([1]),
'end_mask': int64_array([1]), 'ellipsis_mask': int64_array([1]), 'new_axis_mask': int64_array([0]),
'shrink_axis_mask': int64_array([0, 1]),
},
'strided_slice_imag': {
'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice', 'begin_mask': int64_array([1]),
'end_mask': int64_array([1]), 'ellipsis_mask': int64_array([1]), 'new_axis_mask': int64_array([0]),
'shrink_axis_mask': int64_array([0, 1]),
},
'complex': {'kind': 'op', 'op': 'Complex'},
'real_begin': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([0, 0])
},
'imag_begin': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([0, 1])
},
'real_end': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([0, 1])
},
'imag_end': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([0, 2])
},
'real_strides': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([1, 1])
},
'imag_strides': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([1, 1])
},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
non_transformed_graph_edges = [
('placeholder_0', 'strided_slice_real', {'out': 0, 'in': 0}),
('placeholder_1', 'strided_slice_imag', {'out': 0, 'in': 0}),
('strided_slice_real', 'complex', {'in': 0}),
('strided_slice_imag', 'complex', {'in': 1}),
('complex', 'abs'),
('abs', 'output'),
('real_begin', 'strided_slice_real', {'in': 1}),
('imag_begin', 'strided_slice_imag', {'in': 1}),
('real_end', 'strided_slice_real', {'in': 2}),
('imag_end', 'strided_slice_imag', {'in': 2}),
('real_strides', 'strided_slice_real', {'in': 3}),
('imag_strides', 'strided_slice_imag', {'in': 3}),
]
class SSliceComplexTest(unittest.TestCase):
def test_replacement(self):
graph = build_graph(nodes_attrs=graph_node_attrs, edges=graph_edges)
graph.stage = 'front'
SSliceComplex().find_and_replace_pattern(graph)
ref_graph = build_graph(nodes_attrs=ref_graph_node_attrs, edges=ref_graph_edges)
(flag, resp) = compare_graphs(graph, ref_graph, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)
def test_nonreplacement(self):
graph = build_graph(nodes_attrs=non_transformed_graph_node_attrs, edges=non_transformed_graph_edges)
ref_graph = build_graph(nodes_attrs=non_transformed_graph_node_attrs, edges=non_transformed_graph_edges)
graph.stage = 'front'
SSliceComplex().find_and_replace_pattern(graph)
(flag, resp) = compare_graphs(graph, ref_graph, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)
@@ -0,0 +1,72 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
from generator import generator, generate
from extensions.front.tf.TFFFTToDFT import TFFFTToDFT
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
dft_graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'fft': {'kind': 'op', 'op': 'TFFFT', 'num_of_dimensions': 2, 'is_inverse': False},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
}
dft_graph_edges = [
('placeholder', 'fft', {'in': 0}),
('fft', 'abs'),
('abs', 'output'),
]
ref_dft_graph_node_attrs = {
'placeholder': {'shape': int64_array([3, 100, 100, 2]), 'type': 'Parameter', 'kind': 'op', 'op': 'Parameter'},
'fft': {'kind': 'op', 'op': 'DFT'},
'abs': {'type': 'Abs', 'kind': 'op', 'op': 'Abs'},
'output': {'type': None, 'value': None, 'kind': 'op', 'op': 'Result'},
'fft_axes': {
'type': 'Const', 'kind': 'op', 'op': 'Const', 'shape': int64_array([2]), 'value': int64_array([-2, -1])
},
}
ref_dft_graph_edges = [
('placeholder', 'fft', {'in': 0}),
('fft', 'abs'),
('abs', 'output'),
('fft_axes', 'fft', {'in': 1}),
]
@generator
class TFFFTToDFTTest(unittest.TestCase):
@generate(*[(2, False, 'DFT', int64_array([-2, -1])),
(2, True, 'IDFT', int64_array([-2, -1])),
(1, False, 'DFT', int64_array([-1])),
(1, True, 'IDFT', int64_array([-1])),
(3, False, 'DFT', int64_array([-3, -2, -1])),
(3, True, 'IDFT', int64_array([-3, -2, -1]))])
def test_replacement(self, num_of_dimensions, is_inverse, dft_type, fft_axes):
graph = build_graph(nodes_attrs=dft_graph_node_attrs,
edges=dft_graph_edges,
update_attributes={
'fft': {'num_of_dimensions': num_of_dimensions, 'is_inverse': is_inverse},
})
graph.stage = 'front'
setattr(graph.graph['cmd_params'], 'disable_nhwc_to_nchw', False)
graph.graph['layout'] = 'NHWC'
TFFFTToDFT().find_and_replace_pattern(graph)
ref_graph = build_graph(nodes_attrs=ref_dft_graph_node_attrs,
edges=ref_dft_graph_edges,
update_attributes={
'fft': {'kind': 'op', 'op': dft_type},
'fft_axes': {'value': fft_axes, 'shape': int64_array(fft_axes.shape)},
})
(flag, resp) = compare_graphs(graph, ref_graph, 'output', check_op_attrs=True)
self.assertTrue(flag, resp)
@@ -29,44 +29,6 @@ nodes = {
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'),
*connect_front('begin:0', '1:tfslice'),
*connect_front('begin:0', '0:john_doe'),
*connect_front('size:0', '2:tfslice'),
*connect_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=[
*connect_front('input:0', 'slice'),
*connect_front('input:0', 'shapeof'),
*connect_front('begin:0', 'slice:1'),
*connect_front('begin:0', 'john_doe:1'),
*connect_front('begin:0', 'end_const:0'),
*connect_front('size:0', 'end_const:1'),
*connect_front('size:0', 'equal:0'),
*connect_front('shapeof:0', 'select:1'),
*connect_front('minus_one:0', 'equal:1'),
*connect_front('equal:0', 'select:0'),
*connect_front('end_const:0', 'cast:0'),
*connect_front('cast:0', 'select:2'),
*connect_front('select:0', 'slice:2'),
*connect_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=[
*connect_front('input:0', '0:tfslice'),
@@ -0,0 +1,41 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import unittest
import numpy as np
from generator import generator, generate
from extensions.ops.dft import FFTBase
from mo.front.common.partial_infer.utils import int64_array
@generator
class DFTSignalSizeCanonicalizationTest(unittest.TestCase):
@generate(*[
(int64_array([-1, 77]), int64_array([1, 2]), int64_array([2, 180, 180, 2]), int64_array([180, 77])),
(int64_array([390, 87]), int64_array([2, 0]), int64_array([2, 180, 180, 2]), int64_array([390, 87])),
(int64_array([600, -1, 40]),
int64_array([3, 0, 1]),
int64_array([7, 50, 130, 400, 2]),
int64_array([600, 7, 40])),
(int64_array([-1, 16, -1]),
int64_array([3, 0, 2]),
int64_array([7, 50, 130, 400, 2]),
int64_array([400, 16, 130])),
(int64_array([16, -1, -1]),
int64_array([3, 0, 2]),
int64_array([7, 50, 130, 400, 2]),
int64_array([16, 7, 130])),
(int64_array([-1, -1, 16]),
int64_array([3, 0, 2]),
int64_array([7, 50, 130, 400, 2]),
int64_array([400, 7, 16])),
(int64_array([-1, -1, -1]),
int64_array([3, 0, 2]),
int64_array([7, 50, 130, 400, 2]),
int64_array([400, 7, 130])),
])
def test_canonicalization(self, signal_size, axes, input_shape, expected_result):
canonicalized_signal_size = FFTBase.canonicalize_signal_size(signal_size, axes, input_shape)
self.assertTrue(np.array_equal(canonicalized_signal_size, expected_result))