summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Suhov <asuhov@users.noreply.github.com>2019-01-23 20:23:27 +0300
committeropenvino-pushbot <44090433+openvino-pushbot@users.noreply.github.com>2019-01-23 20:23:27 +0300
commit17e66dc5a6631d630da454506902bd7c25d4170b (patch)
tree48ba0f1125b0c15757e8340f7f08f96e90cb0b7e
parent30594bb3099b7874f372074e6c0cc82748344435 (diff)
downloaddldt-17e66dc5a6631d630da454506902bd7c25d4170b.tar.gz
dldt-17e66dc5a6631d630da454506902bd7c25d4170b.tar.bz2
dldt-17e66dc5a6631d630da454506902bd7c25d4170b.zip
Added unit tests and readme for model optimizer (#79)
* added unit tests * added readme for model optimizer * added a list of supported IE plugins
-rw-r--r--inference-engine/README.md15
-rw-r--r--model-optimizer/README.md145
-rw-r--r--model-optimizer/extensions/back/PermuteForReshape_test.py133
-rw-r--r--model-optimizer/extensions/back/ShufflenetReLUReorder_test.py148
-rw-r--r--model-optimizer/extensions/back/TileReshaper_test.py70
-rw-r--r--model-optimizer/extensions/back/insert_compatibility_l2normalization_test.py52
-rw-r--r--model-optimizer/extensions/back/kaldi_remove_memory_output_test.py51
-rw-r--r--model-optimizer/extensions/back/remove_last_softmax_test.py51
-rw-r--r--model-optimizer/extensions/front/caffe/accum_ext_test.py68
-rw-r--r--model-optimizer/extensions/front/caffe/argmax_ext_test.py64
-rw-r--r--model-optimizer/extensions/front/caffe/axpy_test.py44
-rw-r--r--model-optimizer/extensions/front/caffe/bn_test.py67
-rw-r--r--model-optimizer/extensions/front/caffe/conv_ext_test.py349
-rw-r--r--model-optimizer/extensions/front/caffe/correlation_ext_test.py77
-rw-r--r--model-optimizer/extensions/front/caffe/ctcgreedydecoder_ext_test.py61
-rw-r--r--model-optimizer/extensions/front/caffe/data_augmentation_ext_test.py94
-rw-r--r--model-optimizer/extensions/front/caffe/grn_ext_test.py62
-rw-r--r--model-optimizer/extensions/front/caffe/interp_ext_test.py72
-rw-r--r--model-optimizer/extensions/front/caffe/normalize_ext_test.py66
-rw-r--r--model-optimizer/extensions/front/caffe/pooling_ext_test.py112
-rw-r--r--model-optimizer/extensions/front/caffe/power_file_ext_test.py64
-rw-r--r--model-optimizer/extensions/front/caffe/prelu_ext_test.py63
-rw-r--r--model-optimizer/extensions/front/caffe/priorbox_clustered_ext_test.py88
-rw-r--r--model-optimizer/extensions/front/caffe/priorbox_ext_test.py86
-rw-r--r--model-optimizer/extensions/front/caffe/proposal_ext_test.py75
-rw-r--r--model-optimizer/extensions/front/caffe/proposal_python_ext_test.py122
-rw-r--r--model-optimizer/extensions/front/caffe/psroipooling_ext_test.py65
-rw-r--r--model-optimizer/extensions/front/caffe/regionyolo_ext_test.py79
-rw-r--r--model-optimizer/extensions/front/caffe/reorgyolo_ext_test.py61
-rw-r--r--model-optimizer/extensions/front/caffe/resample_ext_test.py72
-rw-r--r--model-optimizer/extensions/front/caffe/simplernms_ext_test.py74
-rw-r--r--model-optimizer/extensions/front/caffe/spatial_transformer_ext_test.py81
-rw-r--r--model-optimizer/extensions/front/eltwise_n_test.py89
-rw-r--r--model-optimizer/extensions/front/freeze_placeholder_value_test.py103
-rw-r--r--model-optimizer/extensions/front/image_scaler_test.py103
-rw-r--r--model-optimizer/extensions/front/instance_normalization_test.py61
-rw-r--r--model-optimizer/extensions/front/kaldi/replace_splice_node_pattern_test.py57
-rw-r--r--model-optimizer/extensions/front/mxnet/check_softmax_node_inputs_test.py60
-rw-r--r--model-optimizer/extensions/front/mxnet/conv_ext_test.py152
-rw-r--r--model-optimizer/extensions/front/mxnet/custom_test.py57
-rw-r--r--model-optimizer/extensions/front/mxnet/pooling_ext_test.py50
-rw-r--r--model-optimizer/extensions/front/mxnet/slice_channel_ext_test.py55
-rw-r--r--model-optimizer/extensions/front/mxnet/ssd_pattern_flatten_softmax_activation_test.py45
-rw-r--r--model-optimizer/extensions/front/mxnet/ssd_pattern_remove_flatten_test.py42
-rw-r--r--model-optimizer/extensions/front/mxnet/ssd_pattern_remove_reshape_test.py56
-rw-r--r--model-optimizer/extensions/front/mxnet/ssd_pattern_remove_transpose_test.py47
-rw-r--r--model-optimizer/extensions/front/mxnet/ssd_reorder_detection_out_inputs_test.py54
-rw-r--r--model-optimizer/extensions/front/onnx/affine_ext_test.py79
-rw-r--r--model-optimizer/extensions/front/onnx/conv_ext_test.py137
-rw-r--r--model-optimizer/extensions/front/onnx/crop_ext_test.py80
-rw-r--r--model-optimizer/extensions/front/onnx/elu_ext_test.py58
-rw-r--r--model-optimizer/extensions/front/onnx/flatten_ext_test.py57
-rw-r--r--model-optimizer/extensions/front/onnx/gather_ext_test.py57
-rw-r--r--model-optimizer/extensions/front/onnx/image_scaler_ext_test.py52
-rw-r--r--model-optimizer/extensions/front/onnx/instance_normalization_ext_test.py44
-rw-r--r--model-optimizer/extensions/front/onnx/pad_ext_test.py80
-rw-r--r--model-optimizer/extensions/front/onnx/sigmoid_ext_test.py62
-rw-r--r--model-optimizer/extensions/front/onnx/slice_ext_test.py75
-rw-r--r--model-optimizer/extensions/front/onnx/squeeze_ext_test.py68
-rw-r--r--model-optimizer/extensions/front/onnx/tanh_ext_test.py62
-rw-r--r--model-optimizer/extensions/front/onnx/transpose_ext_test.py75
-rw-r--r--model-optimizer/extensions/front/onnx/unsqueeze_ext_test.py60
-rw-r--r--model-optimizer/extensions/front/onnx/upsample_ext_test.py104
-rw-r--r--model-optimizer/extensions/front/reciprocal_test.py55
-rw-r--r--model-optimizer/extensions/front/tf/ObjectDetectionAPI_test.py127
-rw-r--r--model-optimizer/extensions/front/tf/concat_ext_test.py34
-rw-r--r--model-optimizer/extensions/front/tf/concat_test.py47
-rw-r--r--model-optimizer/extensions/front/tf/conv_ext_test.py140
-rw-r--r--model-optimizer/extensions/front/tf/deconv_ext_test.py100
-rw-r--r--model-optimizer/extensions/front/tf/fifo_replacer_test.py79
-rw-r--r--model-optimizer/extensions/front/tf/mvn_unrolled_test.py69
-rw-r--r--model-optimizer/extensions/front/tf/next_iteration_ext_test.py30
-rw-r--r--model-optimizer/extensions/front/tf/pad_ext_test.py27
-rw-r--r--model-optimizer/extensions/front/tf/pooling_ext_test.py154
-rw-r--r--model-optimizer/extensions/front/tf/stop_gradient_ext_test.py46
-rw-r--r--model-optimizer/extensions/middle/AddIsCyclicAttribute_test.py50
-rw-r--r--model-optimizer/extensions/middle/AddReshapeAfterStridedSlice_test.py312
-rw-r--r--model-optimizer/extensions/middle/ConvertGroupedStridedSlice_test.py530
-rw-r--r--model-optimizer/extensions/middle/EltwiseInputNormalization_test.py178
-rw-r--r--model-optimizer/extensions/middle/EltwiseInputReshape_test.py225
-rw-r--r--model-optimizer/extensions/middle/FusePermutesSequence_test.py118
-rw-r--r--model-optimizer/extensions/middle/MinumumMiddleReplacer_test.py84
-rw-r--r--model-optimizer/extensions/middle/NormalizeFullyConnected_test.py119
-rw-r--r--model-optimizer/extensions/middle/PixelLinkReshape_test.py110
-rw-r--r--model-optimizer/extensions/middle/Reduce_test.py320
-rw-r--r--model-optimizer/extensions/middle/ShuffleChannel_test.py53
-rw-r--r--model-optimizer/extensions/middle/ShufflenetReshape_test.py200
-rw-r--r--model-optimizer/extensions/middle/SliceConvert_test.py124
-rw-r--r--model-optimizer/extensions/middle/TensorIteratorBackEdge_test.py84
-rw-r--r--model-optimizer/extensions/middle/TensorIteratorCondition_test.py70
-rw-r--r--model-optimizer/extensions/middle/TensorIteratorInput_test.py163
-rw-r--r--model-optimizer/extensions/middle/TensorIteratorOutput_test.py63
-rw-r--r--model-optimizer/extensions/middle/UselessSridedSlice_test.py99
-rw-r--r--model-optimizer/extensions/middle/lstm_sequence_normalize_test.py55
-rw-r--r--model-optimizer/extensions/ops/accum_test.py120
-rw-r--r--model-optimizer/extensions/ops/argmax_test.py145
-rw-r--r--model-optimizer/extensions/ops/assert_test.py53
-rw-r--r--model-optimizer/extensions/ops/correlation_test.py58
-rw-r--r--model-optimizer/extensions/ops/ctc_greedy_decoder_test.py51
-rw-r--r--model-optimizer/extensions/ops/data_augmentation_test.py62
-rw-r--r--model-optimizer/extensions/ops/depth_to_space_test.py57
-rw-r--r--model-optimizer/extensions/ops/gather_test.py59
-rw-r--r--model-optimizer/extensions/ops/grn_test.py45
-rw-r--r--model-optimizer/extensions/ops/instance_normalization_test.py28
-rw-r--r--model-optimizer/extensions/ops/interp_test.py216
-rw-r--r--model-optimizer/extensions/ops/merge_test.py99
-rw-r--r--model-optimizer/extensions/ops/normalize_test.py46
-rw-r--r--model-optimizer/extensions/ops/priorbox_clustered_test.py77
-rw-r--r--model-optimizer/extensions/ops/priorbox_test.py122
-rw-r--r--model-optimizer/extensions/ops/proposal_test.py47
-rw-r--r--model-optimizer/extensions/ops/psroipooling_test.py84
-rw-r--r--model-optimizer/extensions/ops/regionyolo_test.py133
-rw-r--r--model-optimizer/extensions/ops/reorgyolo_test.py57
-rw-r--r--model-optimizer/extensions/ops/resample_test.py94
-rw-r--r--model-optimizer/extensions/ops/select_test.py114
-rw-r--r--model-optimizer/extensions/ops/simplernms_test.py57
-rw-r--r--model-optimizer/extensions/ops/spatial_transformer_test.py71
-rw-r--r--model-optimizer/extensions/ops/switch_test.py234
-rw-r--r--model-optimizer/mo/back/ie_ir_ver_2/emitter_test.py74
-rw-r--r--model-optimizer/mo/front/caffe/custom_layers_mapping_test.py63
-rw-r--r--model-optimizer/mo/front/caffe/extractor_test.py124
-rw-r--r--model-optimizer/mo/front/caffe/extractors/batchnorm_test.py129
-rw-r--r--model-optimizer/mo/front/caffe/extractors/concat_test.py37
-rw-r--r--model-optimizer/mo/front/caffe/extractors/crop_test.py66
-rw-r--r--model-optimizer/mo/front/caffe/extractors/eltwise_test.py93
-rw-r--r--model-optimizer/mo/front/caffe/extractors/elu_test.py53
-rw-r--r--model-optimizer/mo/front/caffe/extractors/inner_product_test.py61
-rw-r--r--model-optimizer/mo/front/caffe/extractors/input_test.py57
-rw-r--r--model-optimizer/mo/front/caffe/extractors/lrn_test.py66
-rw-r--r--model-optimizer/mo/front/caffe/extractors/permute_test.py48
-rw-r--r--model-optimizer/mo/front/caffe/extractors/power_test.py46
-rw-r--r--model-optimizer/mo/front/caffe/extractors/relu_test.py43
-rw-r--r--model-optimizer/mo/front/caffe/extractors/reshape_test.py56
-rw-r--r--model-optimizer/mo/front/caffe/extractors/scale_test.py144
-rw-r--r--model-optimizer/mo/front/caffe/extractors/slice_test.py102
-rw-r--r--model-optimizer/mo/front/caffe/extractors/utils_test.py93
-rw-r--r--model-optimizer/mo/front/caffe/loader_test.py160
-rw-r--r--model-optimizer/mo/front/caffe/python_layer_extractor_test.py61
-rw-r--r--model-optimizer/mo/front/common/layout_test.py115
-rw-r--r--model-optimizer/mo/front/common/partial_infer/caffe_fallback_test.py109
-rw-r--r--model-optimizer/mo/front/common/partial_infer/concat_test.py99
-rw-r--r--model-optimizer/mo/front/common/partial_infer/crop_test.py123
-rw-r--r--model-optimizer/mo/front/common/partial_infer/elemental_test.py39
-rw-r--r--model-optimizer/mo/front/common/partial_infer/eltwise_test.py139
-rw-r--r--model-optimizer/mo/front/common/partial_infer/expand_dims_test.py160
-rw-r--r--model-optimizer/mo/front/common/partial_infer/inner_product_test.py74
-rw-r--r--model-optimizer/mo/front/common/partial_infer/multi_box_detection_test.py129
-rw-r--r--model-optimizer/mo/front/common/partial_infer/multi_box_prior_test.py58
-rw-r--r--model-optimizer/mo/front/common/partial_infer/range_test.py114
-rw-r--r--model-optimizer/mo/front/common/partial_infer/roipooling_test.py92
-rw-r--r--model-optimizer/mo/front/common/partial_infer/slice_test.py381
-rw-r--r--model-optimizer/mo/front/common/partial_infer/split_test.py207
-rw-r--r--model-optimizer/mo/front/extractor_test.py602
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/add_shift_ext_test.py56
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/affine_component_ext_test.py48
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/affine_transform_ext_test.py52
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/common_ext_test.py113
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/concat_ext_test.py30
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/convolutional_component_ext_test.py67
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/fixed_affine_component_ext_test.py49
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/max_pooling_ext_test.py52
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/rescale_ext_test.py51
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/sigmoid_ext_test.py33
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/slice_ext_test.py35
-rw-r--r--model-optimizer/mo/front/kaldi/extractors/tanh_ext_test.py33
-rw-r--r--model-optimizer/mo/front/kaldi/loader/utils_test.py97
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/activation_test.py63
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/crop_test.py44
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/eltwise_test.py44
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/leaky_relu_test.py89
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/multibox_detection_test.py80
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/multibox_prior_test.py56
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/relu_test.py43
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/sigmoid_test.py44
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/slice_axis_test.py51
-rw-r--r--model-optimizer/mo/front/mxnet/extractors/utils_test.py202
-rw-r--r--model-optimizer/mo/front/mxnet/loader_test.py62
-rw-r--r--model-optimizer/mo/front/onnx/extractors/constant_test.py60
-rw-r--r--model-optimizer/mo/front/tf/extractors/concat_test.py38
-rw-r--r--model-optimizer/mo/front/tf/extractors/const_test.py177
-rw-r--r--model-optimizer/mo/front/tf/extractors/eltwise_test.py357
-rw-r--r--model-optimizer/mo/front/tf/extractors/expand_dims_test.py32
-rw-r--r--model-optimizer/mo/front/tf/extractors/identity_test.py32
-rw-r--r--model-optimizer/mo/front/tf/extractors/lrn_test.py53
-rw-r--r--model-optimizer/mo/front/tf/extractors/matmul_test.py43
-rw-r--r--model-optimizer/mo/front/tf/extractors/mean_test.py42
-rw-r--r--model-optimizer/mo/front/tf/extractors/prod_test.py41
-rw-r--r--model-optimizer/mo/front/tf/extractors/squeeze_test.py42
-rw-r--r--model-optimizer/mo/front/tf/extractors/utils_test.py44
-rw-r--r--model-optimizer/mo/front/tf/loader_test.py34
-rw-r--r--model-optimizer/mo/graph/graph_test.py324
-rw-r--r--model-optimizer/mo/main_test.py31
-rw-r--r--model-optimizer/mo/middle/passes/conv_test.py163
-rw-r--r--model-optimizer/mo/middle/passes/eliminate_test.py171
-rw-r--r--model-optimizer/mo/middle/passes/fusing/decomposition_test.py531
-rw-r--r--model-optimizer/mo/middle/passes/fusing/fuse_linear_ops_test.py1201
-rw-r--r--model-optimizer/mo/middle/passes/fusing/fuse_linear_seq_test.py908
-rw-r--r--model-optimizer/mo/middle/passes/fusing/helpers_test.py307
-rw-r--r--model-optimizer/mo/middle/passes/fusing/mark_unfused_nodes_test.py312
-rw-r--r--model-optimizer/mo/middle/passes/fusing/resnet_optimization_test.py646
-rw-r--r--model-optimizer/mo/middle/passes/infer_test.py531
-rw-r--r--model-optimizer/mo/middle/passes/mean_scale_values_test.py157
-rw-r--r--model-optimizer/mo/middle/passes/pool_test.py96
-rw-r--r--model-optimizer/mo/middle/passes/shared_weights_duplication_test.py77
-rw-r--r--model-optimizer/mo/ops/activation_test.py111
-rw-r--r--model-optimizer/mo/ops/clamp_test.py47
-rw-r--r--model-optimizer/mo/ops/concat_test.py47
-rw-r--r--model-optimizer/mo/ops/convolution_test.py371
-rw-r--r--model-optimizer/mo/ops/crop_test.py187
-rw-r--r--model-optimizer/mo/ops/flatten_onnx_test.py65
-rw-r--r--model-optimizer/mo/ops/flatten_test.py62
-rw-r--r--model-optimizer/mo/ops/inner_product_test.py48
-rw-r--r--model-optimizer/mo/ops/pad_test.py95
-rw-r--r--model-optimizer/mo/ops/permute_test.py96
-rw-r--r--model-optimizer/mo/ops/pooling_test.py122
-rw-r--r--model-optimizer/mo/ops/power_test.py100
-rw-r--r--model-optimizer/mo/ops/slice_test.py117
-rw-r--r--model-optimizer/mo/ops/tile_test.py196
-rw-r--r--model-optimizer/mo/ops/unsqueeze_test.py67
-rw-r--r--model-optimizer/mo/pipeline/common_test.py241
-rw-r--r--model-optimizer/mo/pipeline/kaldi_test.py101
-rw-r--r--model-optimizer/mo/pipeline/mx_test.py67
-rw-r--r--model-optimizer/mo/utils/cli_parser_test.py672
-rw-r--r--model-optimizer/mo/utils/convert.py55
-rw-r--r--model-optimizer/mo/utils/graph_test.py214
-rw-r--r--model-optimizer/mo/utils/pipeline_config_test.py150
-rw-r--r--model-optimizer/mo/utils/simple_proto_parser_test.py200
-rw-r--r--model-optimizer/mo/utils/summarize_graph_test.py38
-rw-r--r--model-optimizer/mo/utils/unittest/extractors.py82
-rw-r--r--model-optimizer/mo/utils/unittest/graph.py321
-rw-r--r--model-optimizer/mo/utils/utils_test.py41
-rw-r--r--model-optimizer/mo/utils/version_test.py24
232 files changed, 26804 insertions, 0 deletions
diff --git a/inference-engine/README.md b/inference-engine/README.md
index c122e0098..f0d6641be 100644
--- a/inference-engine/README.md
+++ b/inference-engine/README.md
@@ -1,3 +1,18 @@
+## Repository components
+
+The Inference Engine can infer models in different formats with various input and output formats.
+
+The open source version of Inference Engine includes the following plugins:
+
+| PLUGIN | DEVICE TYPES |
+| ---------------------| -------------|
+| CPU plugin | Intel® Xeon® with Intel® AVX2 and AVX512, Intel® Core™ Processors with Intel® AVX2, Intel® Atom® Processors with Intel® SSE |
+| GPU plugin | Intel® Processor Graphics, including Intel® HD Graphics and Intel® Iris® Graphics |
+| GNA plugin | Intel® Speech Enabling Developer Kit, Amazon Alexa* Premium Far-Field Developer Kit, Intel® Pentium® Silver processor J5005, Intel® Celeron® processor J4005, Intel® Core™ i3-8121U processor |
+| Heterogeneous plugin | Heterogeneous plugin enables computing for inference on one network on several Intel® devices. |
+
+Inference Engine plugins for Intel® FPGA and Intel® Movidius™ Neural Compute Stick are distributed only in a binary form as a part of [Intel® Distribution of OpenVINO™](https://software.intel.com/en-us/openvino-toolkit).
+
## Build on Linux\* Systems
The software was validated on:
diff --git a/model-optimizer/README.md b/model-optimizer/README.md
new file mode 100644
index 000000000..8bfe2176c
--- /dev/null
+++ b/model-optimizer/README.md
@@ -0,0 +1,145 @@
+## Project structure
+
+Project structure:
+<pre>
+ |-- root
+ |-- extensions
+ |-- front/ - graph transformations during front phase
+ |-- middle/ - graph transformations during middle phase (after partial inference)
+ |-- end/ - graph transformations during back phase (before IR generation)
+ |-- ops/ - Model Optimizer operation classes
+ |-- mo
+ |-- back - Back-End logic: contains IR emitting logic
+ |-- front - Front-End logic: contains matching between Framework-specific layers and IR specific,
+ calculation of output shapes for each registered layer
+ |-- graph - Graph utilities to work with internal IR representation
+ |-- middle - Graph transformations - optimizations of the model
+ |-- ops - Model Optimizer operation classes
+ |-- pipeline - Sequence of steps required to create IR for each framework
+ |-- utils - Utility functions
+ |-- tf_call_ie_layer - Sources for TensorFlow fallback in Inference Engine during model inference
+ |-- mo.py - Centralized entry point that can be used for any supported framework
+ |-- mo_caffe.py - Entry point particularly for Caffe
+ |-- mo_mxnet.py - Entry point particularly for MXNet
+ |-- mo_tf.py - Entry point particularly for TensorFlow
+
+</pre>
+
+## Prerequisites
+
+Model Optimizer requires:
+
+1. Python 3.4 or newer
+
+## Installation instructions
+
+1. Go to the Model Optimizer folder
+
+2. Create virtual environment and activate it. This option is strongly recommended as it creates a Python sandbox and
+ dependencies for Model Optimizer do not influence global Python configuration, installed libraries etc. At the same
+ time, special flag ensures that system-wide Python libraries are also available in this sandbox. Skip this
+ step only if you do want to install all Model Optimizer dependencies globally:
+
+ * Create environment:
+ <pre>virtualenv -p /usr/bin/python3.6 .env3 --system-site-packages</pre>
+ * Activate it:
+ <pre>. .env3/bin/activate</pre>
+3. Install dependencies. If you want to convert models only from particular framework, you should use one of
+ available <code>requirements_*.txt</code> files corresponding to the framework of choice. For example, for Caffe use
+ <code>requirements_caffe.txt</code> and so on. When you decide to switch later to other frameworks, please install dependencies
+ for them using the same mechanism:
+ <pre>
+ pip3 install -r requirements.txt
+ </pre>
+
+## Command-Line Interface (CLI)
+
+The following short examples are framework-dependent. Please read the complete help
+with --help option for details across all frameworks:
+<pre>
+ python3 mo.py --help
+</pre>
+
+There are several scripts that convert a model:
+
+1. <code>mo.py</code> -- universal entry point that can convert a model from any supported framework
+
+2. <code>mo_caffe.py</code> -- dedicated script for Caffe models conversion
+
+3. <code>mo_mxnet.py</code> -- dedicated script for MXNet models conversion
+
+4. <code>mo_tf.py</code> -- dedicated script for TensorFlow models conversion
+
+5. <code>mo_onnx.py</code> -- dedicated script for ONNX models conversion
+
+6. <code>mo_kaldi.py</code> -- dedicated script for Kaldi models conversion
+
+<code>mo.py</code> can deduce original framework where input model was trained by an extension of
+the model file. Or <code>--framework</code> option can be used for this purpose if model files
+don't have standard extensions (<code>.pb</code> - for TensorFlow models, <code>.params</code> - for MXNet models,
+<code>.caffemodel</code> - for Caffe models). So, the following commands are equivalent::
+
+<pre>
+ python3 mo.py --input_model /user/models/model.pb
+ python3 mo.py --framework tf --input_model /user/models/model.pb
+</pre>
+The following examples illustrate the shortest command lines to convert a model per
+framework.
+
+### Convert TensorFlow model
+
+To convert a frozen TensorFlow model contained in binary file <code>model-file.pb</code>, run
+dedicated entry point <code>mo_tf.py</code>:
+
+ python3 mo_tf.py --input_model model-file.pb
+
+### Convert Caffe model
+
+To convert a Caffe model contained in <code>model-file.prototxt</code> and <code>model-file.caffemodel</code> run
+dedicated entry point <code>mo_caffe.py</code>:
+<pre>
+ python3 mo_caffe.py --input_model model-file.caffemodel
+</pre>
+
+
+### Convert MXNet model
+
+To Convert an MXNet model in <code>model-file-symbol.json</code> and <code>model-file-0000.params</code> run
+dedicated entry point <code>mo_mxnet.py</code>:
+<pre>
+ python3 mo_mxnet.py --input_model model-file
+</pre>
+
+> **NOTE**: for TensorFlow* all Placeholder ops are represented as Input layers in the final IR.
+
+### Convert ONNX* model
+
+The Model Optimizer assumes that you have an ONNX model that was directly downloaded from a public repository or converted from any framework that supports exporting to the ONNX format.
+
+Use the mo_onnx.py script to simply convert a model with the path to the input model .onnx file:
+
+<pre>
+ python3 mo_onnx.py --input_model model-file.onnx
+</pre>
+
+Input channels re-ordering, scaling, subtraction of mean values and other preprocessing features
+are not applied by default. To pass necessary values to Model Optimizer, please run <code>mo.py</code>
+(or <code>mo_tf.py</code>, <code>mo_caffe.py</code>, <code>mo_mxnet.py</code>) with <code>--help</code> and
+examine all available options.
+
+## Working with Inference Engine
+
+To the moment, Inference Engine is the only consumer of IR models that Model Optimizer produces.
+The whole workflow and more documentation on the structure of IR are documented in the Developer Guide
+of Inference Engine. Note that sections about running Model Optimizer refer to the old version
+of the tool and can not be applied to the current version of Model Optimizer.
+
+### How to run unit-tests
+
+1. Run tests with:
+<pre>
+ python -m unittest discover -p "*_test.py" [-s PATH_TO_DIR]
+</pre>
+
+---
+\* Other names and brands may be claimed as the property of others.
diff --git a/model-optimizer/extensions/back/PermuteForReshape_test.py b/model-optimizer/extensions/back/PermuteForReshape_test.py
new file mode 100644
index 000000000..6efc48293
--- /dev/null
+++ b/model-optimizer/extensions/back/PermuteForReshape_test.py
@@ -0,0 +1,133 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+import numpy as np
+
+from extensions.back.PermuteForReshape import PermuteForReshape
+from mo.graph.graph import Node
+from mo.ops.op import PermuteAttrs
+from mo.utils.unittest.graph import build_graph_with_attrs, compare_graphs
+
+
+class ReshapeToPermuteTest(unittest.TestCase):
+ nodes = [
+ ('input_data', {'kind': 'data', 'shape': None}),
+ ('reshape', {'kind': 'op', 'op': 'Squeeze', 'type': 'Reshape', 'dim': None}),
+ ('reshape_data', {'kind': 'data'}),
+ ]
+ edges = [
+ ('input_data', 'reshape'),
+ ('reshape', 'reshape_data'),
+ ]
+
+ permute_nodes = [
+ ('permute', {'kind': 'op', 'op': 'Permute'}),
+ ('permute_data', {'kind': 'data', 'shape': None})
+ ]
+ permute_edges = [
+ ('input_data', 'permute'),
+ ('permute', 'permute_data'),
+ ('permute_data', 'reshape'),
+ ]
+
+ def test_from3D_to3D(self):
+ input_shape = np.array([2, 3, 4])
+ new_shape = np.array([2, 3, 4])
+ graph = build_graph_with_attrs(
+ nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ update_nodes_attributes=[('input_data', {'shape': input_shape}),
+ ('reshape', {'dim': new_shape}),
+ ('reshape_data', {'shape': new_shape})]
+ )
+ graph.graph['layout'] = 'NHWC'
+ # add permute attrs to reshape
+ reshape = Node(graph, 'reshape')
+ PermuteAttrs.create_permute_attrs(reshape, attrs=[('dim', 'output:0')])
+
+ tested_pattern = PermuteForReshape()
+ tested_pattern.find_and_replace_pattern(graph)
+ (flag, resp) = compare_graphs(graph, graph, last_node='reshape_data')
+ self.assertTrue(flag, resp)
+
+ def test_from4D_to3D(self):
+ input_shape = np.array([1, 2, 3, 4])
+ new_shape = np.array([3, 4, 2])
+ nhwc_shape = np.array([1, 3, 4, 2])
+ graph = build_graph_with_attrs(
+ nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ update_nodes_attributes=[('input_data', {'shape': input_shape}),
+ ('reshape', {'dim': new_shape}),
+ ('reshape_data', {'shape': new_shape})]
+ )
+ graph.graph['layout'] = 'NHWC'
+ # add permute attrs to reshape
+ reshape = Node(graph, 'reshape')
+ PermuteAttrs.create_permute_attrs(reshape, attrs=[('dim', 'output:0')])
+
+ tested_pattern = PermuteForReshape()
+ tested_pattern.find_and_replace_pattern(graph)
+ graph_ref = build_graph_with_attrs(
+ nodes_with_attrs=self.nodes + self.permute_nodes,
+ edges_with_attrs=self.edges[1:] + self.permute_edges,
+ update_nodes_attributes=[('input_data', {'shape': input_shape}),
+ ('reshape', {'dim': new_shape}),
+ ('reshape_data', {'shape': new_shape}),
+ ('permute_data', {'shape': nhwc_shape})]
+ )
+ # check graphs equality
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='reshape_data')
+ self.assertTrue(flag, resp)
+
+ # check righ order in new permutation node
+ permute_order = graph.node['reshape/Permute_']['order']
+ self.assertTrue(np.all(permute_order == np.array([0, 2, 3, 1]))) # from NCHW to NHWC
+
+ def test_from_5D_to_3D(self):
+ input_shape = np.array([1, 2, 1, 3, 4]) # NCDHW 1 1 3 4 2
+ new_shape = np.array([3, 4, 2])
+ nhwc_shape = np.array([1, 1, 3, 4, 2])
+ graph = build_graph_with_attrs(
+ nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ update_nodes_attributes=[('input_data', {'shape': input_shape}),
+ ('reshape', {'dim': new_shape}),
+ ('reshape_data', {'shape': new_shape})]
+ )
+ graph.graph['layout'] = 'NHWC'
+ # add permute attrs to reshape
+ reshape = Node(graph, 'reshape')
+ PermuteAttrs.create_permute_attrs(reshape, attrs=[('dim', 'output:0')])
+
+ tested_pattern = PermuteForReshape()
+ tested_pattern.find_and_replace_pattern(graph)
+ graph_ref = build_graph_with_attrs(
+ nodes_with_attrs=self.nodes + self.permute_nodes,
+ edges_with_attrs=self.edges[1:] + self.permute_edges,
+ update_nodes_attributes=[('input_data', {'shape': input_shape}),
+ ('reshape', {'dim': new_shape}),
+ ('reshape_data', {'shape': new_shape}),
+ ('permute_data', {'shape': nhwc_shape})]
+ )
+ # check graphs equality
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='reshape_data')
+ self.assertTrue(flag, resp)
+
+ # check righ order in new permutation node
+ permute_order = graph.node['reshape/Permute_']['order']
+ self.assertTrue(np.all(permute_order == np.array([0, 2, 3, 4, 1]))) # from NCDHW to NDHWC
diff --git a/model-optimizer/extensions/back/ShufflenetReLUReorder_test.py b/model-optimizer/extensions/back/ShufflenetReLUReorder_test.py
new file mode 100644
index 000000000..27c0f34cc
--- /dev/null
+++ b/model-optimizer/extensions/back/ShufflenetReLUReorder_test.py
@@ -0,0 +1,148 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.back.ShufflenetReLUReorder import ShufflenetReLUReorder
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+# The dictionary with nodes attributes used to build various graphs. A key is the name of the node and the value is the
+# dictionary with node attributes.
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ReLU
+ 'relu_1': {'type': 'ReLU', 'kind': 'op', 'op': 'ReLU'},
+ 'relu_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Reshape layers
+ 'reshape_1': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'reshape_2': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'reshape_3': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_3_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Transpose layer
+ 'transpose_1': {'type': 'Permute', 'kind': 'op', 'op': 'Transpose'},
+ 'transpose_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Conv layer
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2d'},
+ 'conv_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class ShufflenetReLUReorderTests(unittest.TestCase):
+ def test_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'relu_1'),
+ ('relu_1', 'relu_1_data'),
+ ('relu_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'transpose_1'),
+ ('transpose_1', 'transpose_1_data'),
+ ('transpose_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'conv_1'),
+ ('conv_1', 'conv_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'relu_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'reshape_1_data': {'shape': np.array([227, 227, 4, 28])},
+ 'transpose_1': {'order': np.array([0, 1, 3, 2])},
+ 'transpose_1_data': {'shape': np.array([227, 227, 28, 4])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 112])},
+ 'conv_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'conv_1': {'pad': np.array([1, 1])}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'transpose_1'),
+ ('transpose_1', 'transpose_1_data'),
+ ('transpose_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'relu_1'),
+ ('relu_1', 'relu_1_data'),
+ ('relu_1_data', 'conv_1'),
+ ('conv_1', 'conv_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'relu_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'reshape_1_data': {'shape': np.array([227, 227, 4, 28])},
+ 'transpose_1': {'order': np.array([0, 1, 3, 2])},
+ 'transpose_1_data': {'shape': np.array([227, 227, 28, 4])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 112])},
+ 'conv_1_data': {'shape': np.array([1, 227, 227, 112])},
+ })
+
+ pattern = ShufflenetReLUReorder()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_2_neg(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'transpose_1'),
+ ('transpose_1', 'transpose_1_data'),
+ ('transpose_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'conv_1'),
+ ('conv_1', 'conv_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'relu_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'reshape_1_data': {'shape': np.array([227, 227, 4, 28])},
+ 'transpose_1': {'order': np.array([0, 1, 3, 2])},
+ 'transpose_1_data': {'shape': np.array([227, 227, 28, 4])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 112])},
+ 'conv_1_data': {'shape': np.array([1, 227, 227, 112])},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'transpose_1'),
+ ('transpose_1', 'transpose_1_data'),
+ ('transpose_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'conv_1'),
+ ('conv_1', 'conv_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'relu_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'reshape_1_data': {'shape': np.array([227, 227, 4, 28])},
+ 'transpose_1': {'order': np.array([0, 1, 3, 2])},
+ 'transpose_1_data': {'shape': np.array([227, 227, 28, 4])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 112])},
+ 'conv_1_data': {'shape': np.array([1, 227, 227, 112])},
+ })
+
+ pattern = ShufflenetReLUReorder()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/back/TileReshaper_test.py b/model-optimizer/extensions/back/TileReshaper_test.py
new file mode 100644
index 000000000..5c432190e
--- /dev/null
+++ b/model-optimizer/extensions/back/TileReshaper_test.py
@@ -0,0 +1,70 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.back.TileReshaper import TileReshaper
+from mo.ops.tile import Tile
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+# The dictionary with nodes attributes used to build various graphs. A key is the name of the node and the value is the
+# dictionary with node attributes.
+nodes_attributes = {
+ 'previous_data': {'shape': np.array([1, 1, 101]), 'kind': 'data'},
+ 'tile': {'type': 'Tile', 'kind': 'op', 'axis': 1, 'tiles': 16, 'infer': Tile.infer},
+ 'tile_data': {'shape': np.array([1, 16, 101]), 'kind': 'data'},
+ 'next_op': {'kind': 'op', 'op': 'SomeOp'},
+}
+edge_attributes = [
+ ('previous_data', 'tile'),
+ ('tile', 'tile_data'),
+ ('tile_data', 'next_op'),
+]
+
+nodes_attributes_ref = {
+ 'previous_data': {'kind': 'data', 'shape': np.array([1, 1, 101])},
+ 'reshape_op_before': {'type': 'Reshape', 'kind': 'op', 'dim': [1, 1, 101, 1]},
+ 'reshape_data_before': {'kind': 'data', 'shape': np.array([1, 1, 101, 1])},
+ 'tile': {'type': 'Tile', 'kind': 'op', 'infer': Tile.infer, 'axis': 1, 'tiles': 16},
+ 'tile_data': {'shape': np.array([1, 16, 101, 1]), 'kind': 'data'},
+ 'reshape_op_after': {'type': 'Reshape', 'kind': 'op', 'dim': [1, 16, 101]},
+ 'reshape_data_after': {'kind': 'data', 'shape': np.array([1, 16, 101])},
+ 'next_op': {'kind': 'op', 'op': 'SomeOp'},
+}
+edge_attributes_ref = [
+ ('previous_data', 'reshape_op_before'),
+ ('reshape_op_before', 'reshape_data_before'),
+ ('reshape_data_before', 'tile'),
+ ('tile', 'tile_data'),
+ ('tile_data', 'reshape_op_after'),
+ ('reshape_op_after', 'reshape_data_after'),
+ ('reshape_data_after', 'next_op')
+]
+
+
+class TileReshaperTests(unittest.TestCase):
+ def test_tile_reshaper(self):
+ graph = build_graph(nodes_attributes, edge_attributes)
+
+ graph_ref = build_graph(nodes_attributes_ref, edge_attributes_ref)
+
+ pattern = TileReshaper()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'next_op', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/back/insert_compatibility_l2normalization_test.py b/model-optimizer/extensions/back/insert_compatibility_l2normalization_test.py
new file mode 100644
index 000000000..a1296ac09
--- /dev/null
+++ b/model-optimizer/extensions/back/insert_compatibility_l2normalization_test.py
@@ -0,0 +1,52 @@
+"""
+ Copyright (c) 2017-2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.back.insert_compatibility_l2normalization import CompatibilityL2NormalizationPattern
+from mo.utils.unittest.graph import build_graph
+
+
+class CompatibilityL2NormalizationPatternTest(unittest.TestCase):
+ nodes = {
+ 'input_node': {
+ 'kind': 'data'
+ },
+ 'l2norm_node': {
+ 'op': 'Normalize',
+ 'kind': 'op',
+ 'type': 'Normalize',
+ },
+ 'output_node': {
+ 'kind': 'data'
+ }
+ }
+
+ def test_insert_data(self):
+ graph = build_graph(self.nodes, [('input_node', 'l2norm_node'), ('l2norm_node', 'output_node')],
+ {'input_node': {'shape': np.array([1, 10])},
+ })
+ CompatibilityL2NormalizationPattern().find_and_replace_pattern(graph)
+ self.assertEqual(len(graph.nodes()), 4)
+ self.assertEqual(graph.node['l2norm_node_weights']['name'], 'l2norm_node_weights')
+ self.assertEqual(len(graph.node['l2norm_node_weights']['value']), 10)
+
+ expect_value = np.full([10], 1.0, np.float32)
+
+ for i, val in enumerate(expect_value):
+ self.assertEqual(graph.node['l2norm_node_weights']['value'][i], val)
diff --git a/model-optimizer/extensions/back/kaldi_remove_memory_output_test.py b/model-optimizer/extensions/back/kaldi_remove_memory_output_test.py
new file mode 100644
index 000000000..c72351c23
--- /dev/null
+++ b/model-optimizer/extensions/back/kaldi_remove_memory_output_test.py
@@ -0,0 +1,51 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.back.kaldi_remove_memory_output import KaldiRemoveMemoryOutputBackReplacementPattern
+from mo.utils.unittest.graph import build_graph
+
+
+class KaldiRemoveMemoryOutputTest(unittest.TestCase):
+ nodes = {
+ 'input_node': {
+ 'kind': 'data'
+ },
+ 'memory_node': {
+ 'op': 'Memory',
+ 'kind': 'op'
+ },
+ 'output_node': {
+ 'kind': 'data'
+ }
+ }
+
+ def test_remove_out_data_for_memory(self):
+ graph = build_graph(self.nodes, [('input_node', 'memory_node')])
+ # Need for matching in pattern. The edge memory_node->out_node must contain only the attribute 'out' = 0
+ # build_graph creates edge memory_node->out_node with attributes 'in' and 'out'
+ graph.add_node('output_node', is_output=True, **self.nodes['output_node'])
+ graph.add_edge('memory_node', 'output_node', out=0)
+ KaldiRemoveMemoryOutputBackReplacementPattern().find_and_replace_pattern(graph)
+ self.assertNotIn('output_node', graph.node)
+
+ def test_do_not_remove_out_data_for_memory(self):
+ graph = build_graph(self.nodes, [('input_node', 'memory_node')])
+ graph.add_node('output_node', **self.nodes['output_node'])
+ graph.add_edge('memory_node', 'output_node', out=0)
+ KaldiRemoveMemoryOutputBackReplacementPattern().find_and_replace_pattern(graph)
+ self.assertIn('output_node', graph.node)
diff --git a/model-optimizer/extensions/back/remove_last_softmax_test.py b/model-optimizer/extensions/back/remove_last_softmax_test.py
new file mode 100644
index 000000000..29a01737a
--- /dev/null
+++ b/model-optimizer/extensions/back/remove_last_softmax_test.py
@@ -0,0 +1,51 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.back.remove_last_softmax_pattern import RemoveLastSoftMaxPattern
+from mo.utils.unittest.graph import build_graph
+
+
+class KaldiRemoveLastSoftMaxTest(unittest.TestCase):
+ nodes = {
+ 'input_node': {
+ 'kind': 'data'
+ },
+ 'softmax_node': {
+ 'op': 'SoftMax',
+ 'kind': 'op'
+ },
+ 'output_node': {
+ 'kind': 'data'
+ }
+ }
+
+ def test_remove_last_SoftMax(self):
+ graph = build_graph(self.nodes, [
+ ('input_node', 'softmax_node'),
+ ('softmax_node', 'output_node')
+ ], {'output_node': {'is_output': True}})
+ RemoveLastSoftMaxPattern().find_and_replace_pattern(graph)
+ self.assertNotIn('softmax_node', graph.node)
+
+ def test_do_not_remove_no_last_SoftMax(self):
+ graph = build_graph(self.nodes, [
+ ('input_node', 'softmax_node'),
+ ('softmax_node', 'output_node')
+ ])
+ RemoveLastSoftMaxPattern().find_and_replace_pattern(graph)
+ self.assertIn('softmax_node', graph.node)
diff --git a/model-optimizer/extensions/front/caffe/accum_ext_test.py b/model-optimizer/extensions/front/caffe/accum_ext_test.py
new file mode 100644
index 000000000..f67e745c9
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/accum_ext_test.py
@@ -0,0 +1,68 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.accum_ext import AccumFrontExtractor
+from extensions.ops.accum import AccumOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeAccumProtoLayer:
+ def __init__(self, val):
+ self.accum_param = val
+
+
+class TestAccumExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Accum'] = AccumOp
+
+ def test_accum_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, AccumFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.accum_ext.collect_attributes')
+ def test_accum_ext(self, collect_attributes_mock):
+ params = {
+ 'top_height': 200,
+ 'top_width': 300,
+ 'size_divisible_by': 3,
+ 'have_reference': 'False',
+ }
+ collect_attributes_mock.return_value = {
+ **params,
+ 'have_reference': 0
+ }
+
+ fake_pl = FakeAccumProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ AccumFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "Accum",
+ 'top_height': 200,
+ 'top_width': 300,
+ 'size_divisible_by': 3,
+ 'have_reference': 0,
+ 'infer': AccumOp.accum_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/argmax_ext_test.py b/model-optimizer/extensions/front/caffe/argmax_ext_test.py
new file mode 100644
index 000000000..39547d1c8
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/argmax_ext_test.py
@@ -0,0 +1,64 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.argmax_ext import ArgMaxFrontExtractor
+from extensions.ops.argmax import ArgMaxOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeArgMaxProtoLayer:
+ def __init__(self, val):
+ self.argmax_param = val
+
+
+class TestArgMaxExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['ArgMax'] = ArgMaxOp
+
+ def test_argmax_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, ArgMaxFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.argmax_ext.merge_attrs')
+ def test_argmax_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'out_max_val': True,
+ 'top_k': 100,
+ 'axis': 2
+ }
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakeArgMaxProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ ArgMaxFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'out_max_val': True,
+ 'top_k': 100,
+ 'axis': 2,
+ 'infer': ArgMaxOp.argmax_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/axpy_test.py b/model-optimizer/extensions/front/caffe/axpy_test.py
new file mode 100644
index 000000000..01e535cdf
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/axpy_test.py
@@ -0,0 +1,44 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+from extensions.front.caffe.axpy import AxpyToEltwise
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph_with_edge_attrs
+
+
+class TestAxpyReplacer(unittest.TestCase):
+ def test_axpy(self):
+ nodes = {
+ 'node_1': {'kind': 'op', 'type': 'Identity', 'op': 'Placeholder'},
+ 'node_2': {'kind': 'op', 'type': 'Identity', 'op': 'Placeholder'},
+ 'node_3': {'kind': 'op', 'type': 'Identity', 'op': 'Placeholder'},
+ 'axpy': {'type': 'Axpy', 'kind': 'op', 'op': 'Axpy'},
+ 'node_4': {'kind': 'op', 'type': 'Identity', 'op': 'Placeholder'}}
+ edges = [
+ ('node_1', 'axpy', {'in': 0}),
+ ('node_2', 'axpy', {'in': 1}),
+ ('node_3', 'axpy', {'in': 2}),
+ ('axpy', 'node_4', {'in': 0})]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ node = Node(graph, 'axpy')
+ replacer = AxpyToEltwise()
+ replacer.replace_op(graph, node)
+
+ scale_node = [node for node, attrs in list(graph.nodes(data=True)) if attrs['type'] == 'ScaleShift']
+ self.assertEqual(len(scale_node), 1)
+ add_node = [node for node, attrs in list(graph.nodes(data=True)) if attrs['type'] == 'Eltwise']
+ self.assertEqual(len(add_node), 1)
diff --git a/model-optimizer/extensions/front/caffe/bn_test.py b/model-optimizer/extensions/front/caffe/bn_test.py
new file mode 100644
index 000000000..f075e5033
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/bn_test.py
@@ -0,0 +1,67 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import numpy as np
+import unittest
+
+from extensions.front.caffe.bn import BNToScaleShift
+from mo.graph.graph import Node
+from mo.utils.unittest.extractors import FakeParam
+from mo.utils.unittest.graph import build_graph_with_edge_attrs
+
+
+class FakeBNProtoLayer:
+ def __init__(self, val):
+ self.bn_param = val
+
+
+class FakeBNBinLayer:
+ def __init__(self, val):
+ self.blobs = val
+
+
+class TestBNReplacer(unittest.TestCase):
+ def test_bn(self):
+ bn_pb = FakeBNProtoLayer(FakeParam('eps', 0.0001))
+ mean = [1, 2.5, 3]
+ var = [0.5, 0.1, 1.2]
+ scale = [2.3, 3.4, 4.5]
+ shift = [0.8, 0.6, 0.4]
+ bn_bin = FakeBNBinLayer([FakeParam('data', mean),
+ FakeParam('data', var),
+ FakeParam('data', scale),
+ FakeParam('data', shift)])
+ nodes = {
+ 'node_1': {'kind': 'op', 'type': 'Identity', 'op': 'Placeholder'},
+ 'bn': {'type': 'BN', 'kind': 'op', 'op': 'BN',
+ 'pb': bn_pb,
+ 'model_pb': bn_bin},
+ 'node_2': {'kind': 'op', 'type': 'Identity', 'op': 'Placeholder'}}
+ edges = [
+ ('node_1', 'bn', {'in': 0}),
+ ('bn', 'node_2', {'in': 0})]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ node = Node(graph, 'bn')
+ replacer = BNToScaleShift()
+ replacer.replace_op(graph, node)
+
+ scale_node = [node for node, attrs in list(graph.nodes(data=True)) if attrs['type'] == 'ScaleShift']
+ self.assertEqual(len(scale_node), 1)
+
+ scale_ref = np.array([1.11796412, 3.2272172, 4.74282367])
+ shift_ref = np.array([-2.07131747, -10.87253847, -20.14270653])
+ for i in range(len(mean)):
+ self.assertAlmostEqual(graph.node[scale_node[0]]['scale'][i], scale_ref[i])
+ self.assertAlmostEqual(graph.node[scale_node[0]]['bias'][i], shift_ref[i])
diff --git a/model-optimizer/extensions/front/caffe/conv_ext_test.py b/model-optimizer/extensions/front/caffe/conv_ext_test.py
new file mode 100644
index 000000000..49c8b0b4a
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/conv_ext_test.py
@@ -0,0 +1,349 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+import numpy as np
+
+from extensions.front.caffe.conv_ext import ConvFrontExtractor, DeconvFrontExtractor, conv_create_attrs, conv_set_params
+from mo.front.caffe.extractors.utils import get_list_from_container
+from mo.utils.error import Error
+from mo.utils.unittest.extractors import PB, FakeParam, FakeMultiParam
+
+
+class FakeConvProtoLayer:
+ def __init__(self, val):
+ self.convolution_param = val
+ self.bottom = [0]
+
+
+class TestConvShapesParsing(unittest.TestCase):
+ def test_conv_no_pb_no_ml(self):
+ node = PB({'pb': None})
+ self.assertRaises(Error, ConvFrontExtractor.extract, node)
+
+ @patch('extensions.front.caffe.conv_ext.weights_biases')
+ @patch('extensions.front.caffe.conv_ext.layout_attrs')
+ def test_conv_ext_ideal_numbers(self, weights_biases_mock, layout_attrs_mock):
+ weights_biases_mock.return_value = {}
+ layout_attrs_mock.return_value = {}
+ params = {
+ 'pad': 10,
+ 'kernel_size': 11,
+ 'stride': 12,
+ 'dilation': 13,
+ 'group': 14,
+ 'num_output': 15,
+ 'bias_term': True
+ }
+ node = PB({'pb': FakeConvProtoLayer(FakeMultiParam(params))})
+ ConvFrontExtractor.extract(node)
+ res = node
+ exp_res = {
+ 'op': 'Conv2D',
+ 'pad': np.array([[0, 0], [0, 0], [10, 10], [10, 10]]),
+ 'pad_spatial_shape': np.array([[10, 10], [10, 10]]),
+ 'stride': np.array([1, 1, 12, 12]),
+ 'kernel_spatial': np.array([11, 11]),
+ 'dilation': np.array([1, 1, 13, 13]),
+ 'group': 14,
+ 'bias_addable': True,
+ 'bias_term': True,
+ }
+ self.assertTrue(weights_biases_mock.called)
+ self.assertTrue(layout_attrs_mock.called)
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation'):
+ np.testing.assert_equal(res[key], exp_res[key])
+ else:
+ self.assertEqual(res[key], exp_res[key])
+
+ @patch('extensions.front.caffe.conv_ext.weights_biases')
+ @patch('extensions.front.caffe.conv_ext.layout_attrs')
+ def test_conv_ext_empty_numbers(self, weights_biases_mock, layout_attrs_mock):
+ weights_biases_mock.return_value = {}
+ layout_attrs_mock.return_value = {}
+ params = {
+ 'pad': None,
+ 'kernel_size': None,
+ 'stride': None,
+ 'dilation': None,
+ 'group': 14,
+ 'num_output': 15,
+ 'bias_term': True,
+ 'pad_w': 3,
+ 'pad_h': 4,
+ 'kernel_w': 5,
+ 'kernel_h': 6,
+ 'stride_h': 3,
+ 'stride_w': 2,
+ }
+ node = PB({'pb': FakeConvProtoLayer(FakeMultiParam(params))})
+ ConvFrontExtractor.extract(node)
+ res = node
+ exp_res = {
+ 'op': 'Conv2D',
+ 'pad': np.array([[0, 0], [0, 0], [4, 4], [3, 3]]),
+ 'pad_spatial_shape': np.array([[4, 4], [3, 3]]),
+ 'stride': np.array([1, 1, 3, 2]),
+ 'kernel_spatial': np.array([6, 5]),
+ 'dilation': np.array([1, 1, 1, 1]),
+ 'group': 14,
+ 'bias_addable': True,
+ 'bias_term': True,
+ }
+ self.assertTrue(weights_biases_mock.called)
+ self.assertTrue(layout_attrs_mock.called)
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation'):
+ np.testing.assert_equal(res[key], exp_res[key])
+ else:
+ self.assertEqual(res[key], exp_res[key])
+
+ def test_attrs(self):
+ params = {
+ 'type_str': 'Conv2D',
+ 'padding': [10, 10],
+ 'stride': [12, 12],
+ 'kernel': [11, 11],
+ 'dilate': [13, 13],
+ 'group': 14,
+ 'output': 13,
+ 'bias_term': True
+ }
+
+ res = conv_create_attrs(params)
+
+ exp_res = {
+ 'pad': np.array([[0, 0], [0, 0], [10, 10], [10, 10]]),
+ 'pad_spatial_shape': np.array([[10, 10], [10, 10]]),
+ 'stride': np.array([1, 1, 12, 12]),
+ 'kernel_spatial': np.array([11, 11]),
+ 'dilation': np.array([1, 1, 13, 13]),
+ 'group': 14,
+ 'bias_addable': True,
+ 'bias_term': True,
+ 'output_spatial_shape': None,
+ 'output_shape': None,
+ 'output': 13,
+ }
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation'):
+ np.testing.assert_equal(res[key], exp_res[key])
+ else:
+ self.assertEqual(res[key], exp_res[key])
+
+ def test_get_list_from_container_no_existing_param(self):
+ res = get_list_from_container(FakeParam("p", "1"), 'prop', int)
+ self.assertEqual(res, [])
+
+ def test_get_list_from_container_no_param(self):
+ res = get_list_from_container(None, 'prop', int)
+ self.assertEqual(res, [])
+
+ def test_get_list_from_container_simple_type_match(self):
+ res = get_list_from_container(FakeParam('prop', 10), 'prop', int)
+ self.assertEqual(res, [10])
+
+ def test_get_list_from_container_list_match(self):
+ res = get_list_from_container(FakeParam('prop', [10, 11]), 'prop', int)
+ self.assertEqual(res, [10, 11])
+
+ def test_get_list_from_container_list_match_empty(self):
+ res = get_list_from_container(FakeParam('prop', []), 'prop', int)
+ self.assertEqual(res, [])
+
+ def test_params_creation(self):
+ params = {
+ 'pad': None,
+ 'kernel_size': None,
+ 'stride': None,
+ 'dilation': None,
+ 'group': 14,
+ 'num_output': 15,
+ 'bias_term': True,
+ 'pad_w': 3,
+ 'pad_h': 4,
+ 'kernel_w': 5,
+ 'kernel_h': 6,
+ 'stride_h': 3,
+ 'stride_w': 2,
+ }
+ exp_res = {
+ 'padding': [3, 4],
+ 'stride': [2, 3],
+ 'kernel': [5, 6],
+ 'dilate': [1, 1],
+ 'group': 14,
+ 'output': 15
+ }
+ res = conv_set_params(FakeConvProtoLayer(FakeMultiParam(params)).convolution_param, 'Conv2D')
+
+ for key in exp_res.keys():
+ if key in ('padding', 'stride', 'stride', 'kernel', 'dilate'):
+ np.testing.assert_equal(res[key], exp_res[key])
+ else:
+ self.assertEqual(res[key], exp_res[key])
+
+
+class TestDeconvShapesParsing(unittest.TestCase):
+ def test_deconv_no_pb_no_ml(self):
+ node = PB({'pb': None})
+ self.assertRaises(Error, DeconvFrontExtractor.extract, node)
+
+ @patch('extensions.front.caffe.conv_ext.weights_biases')
+ @patch('extensions.front.caffe.conv_ext.layout_attrs')
+ def test_conv_ext_ideal_numbers(self, weights_biases_mock, layout_attrs_mock):
+ weights_biases_mock.return_value = {}
+ layout_attrs_mock.return_value = {}
+ params = {
+ 'pad': 10,
+ 'kernel_size': 11,
+ 'stride': 12,
+ 'dilation': 13,
+ 'group': 14,
+ 'num_output': 15,
+ 'bias_term': True
+ }
+ node = PB({'pb': FakeConvProtoLayer(FakeMultiParam(params))})
+ res = DeconvFrontExtractor.extract(node)
+ res = node
+ exp_res = {
+ 'op': 'Deconv2D',
+ 'pad': np.array([[0, 0], [0, 0], [10, 10], [10, 10]]),
+ 'pad_spatial_shape': np.array([[10, 10], [10, 10]]),
+ 'stride': np.array([1, 1, 12, 12]),
+ 'kernel_spatial': np.array([11, 11]),
+ 'dilation': np.array([1, 1, 13, 13]),
+ 'group': 14,
+ 'bias_addable': True,
+ }
+ self.assertTrue(weights_biases_mock.called)
+ self.assertTrue(layout_attrs_mock.called)
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation'):
+ np.testing.assert_equal(res[key], exp_res[key])
+ else:
+ self.assertEqual(res[key], exp_res[key])
+
+ @patch('extensions.front.caffe.conv_ext.weights_biases')
+ @patch('extensions.front.caffe.conv_ext.layout_attrs')
+ def test_conv_ext_false_bias_term(self, weights_biases_mock, layout_attrs_mock):
+ weights_biases_mock.return_value = {}
+ layout_attrs_mock.return_value = {}
+ params = {
+ 'pad': 10,
+ 'kernel_size': 11,
+ 'stride': 12,
+ 'dilation': 13,
+ 'group': 14,
+ 'num_output': 15,
+ 'bias_term': False
+ }
+ node = PB({'pb': FakeConvProtoLayer(FakeMultiParam(params))})
+ res = DeconvFrontExtractor.extract(node)
+ res = node
+ exp_res = {
+ 'op': 'Deconv2D',
+ 'pad': np.array([[0, 0], [0, 0], [10, 10], [10, 10]]),
+ 'pad_spatial_shape': np.array([[10, 10], [10, 10]]),
+ 'stride': np.array([1, 1, 12, 12]),
+ 'kernel_spatial': np.array([11, 11]),
+ 'dilation': np.array([1, 1, 13, 13]),
+ 'group': 14,
+ 'bias_addable': True,
+ 'bias_term': False,
+ }
+ self.assertTrue(weights_biases_mock.called)
+ self.assertTrue(layout_attrs_mock.called)
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation', 'bias_term'):
+ np.testing.assert_equal(res[key], exp_res[key])
+ else:
+ self.assertEqual(res[key], exp_res[key])
+
+ @patch('extensions.front.caffe.conv_ext.weights_biases')
+ @patch('extensions.front.caffe.conv_ext.layout_attrs')
+ def test_conv_ext_empty_numbers(self, weights_biases_mock, layout_attrs_mock):
+ weights_biases_mock.return_value = {}
+ layout_attrs_mock.return_value = {}
+ params = {
+ 'pad': None,
+ 'kernel_size': None,
+ 'stride': None,
+ 'dilation': None,
+ 'group': 14,
+ 'num_output': 15,
+ 'bias_term': True,
+ 'pad_w': 3,
+ 'pad_h': 4,
+ 'kernel_w': 5,
+ 'kernel_h': 6,
+ 'stride_h': 3,
+ 'stride_w': 2,
+ }
+ node = PB({'pb': FakeConvProtoLayer(FakeMultiParam(params))})
+ res = DeconvFrontExtractor.extract(node)
+ res = node
+ exp_res = {
+ 'op': 'Deconv2D',
+ 'pad': np.array([[0, 0], [0, 0], [4, 4], [3, 3]]),
+ 'pad_spatial_shape': np.array([[4, 4], [3, 3]]),
+ 'stride': np.array([1, 1, 3, 2]),
+ 'kernel_spatial': np.array([6, 5]),
+ 'dilation': np.array([1, 1, 1, 1]),
+ 'group': 14,
+ 'bias_addable': True,
+ }
+ self.assertTrue(weights_biases_mock.called)
+ self.assertTrue(layout_attrs_mock.called)
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation'):
+ np.testing.assert_equal(res[key], exp_res[key])
+ else:
+ self.assertEqual(res[key], exp_res[key])
+
+ def test_attrs(self):
+ params = {
+ 'type_str': 'Deconv2D',
+ 'padding': [10, 10],
+ 'stride': [12, 12],
+ 'kernel': [11, 11],
+ 'dilate': [13, 13],
+ 'group': 14,
+ 'output': 13,
+ 'bias_term': True
+ }
+ res = conv_create_attrs(params)
+
+ exp_res = {
+ 'pad': np.array([[0, 0], [0, 0], [10, 10], [10, 10]]),
+ 'pad_spatial_shape': np.array([[10, 10], [10, 10]]),
+ 'stride': np.array([1, 1, 12, 12]),
+ 'kernel_spatial': np.array([11, 11]),
+ 'dilation': np.array([1, 1, 13, 13]),
+ 'group': 14,
+ 'bias_addable': True,
+ 'output_spatial_shape': None,
+ 'output_shape': None,
+ 'output': 13,
+ }
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation'):
+ np.testing.assert_equal(res[key], exp_res[key])
+ else:
+ self.assertEqual(res[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/correlation_ext_test.py b/model-optimizer/extensions/front/caffe/correlation_ext_test.py
new file mode 100644
index 000000000..de4b74ca7
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/correlation_ext_test.py
@@ -0,0 +1,77 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.correlation_ext import CorrelationFrontExtractor
+from extensions.ops.correlation import CorrelationOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeCorrProtoLayer:
+ def __init__(self, val):
+ self.correlation_param = val
+
+
+class TestCorrelationExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Correlation'] = CorrelationOp
+
+ def test_da_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, CorrelationFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.correlation_ext.merge_attrs')
+ def test_resample_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'pad': 20,
+ 'kernel_size': 1,
+ 'max_displacement': 20,
+ 'stride_1': 1,
+ 'stride_2': 2,
+ 'single_direction': 0,
+ 'do_abs': False,
+ 'correlation_type': 'caffe.CorrelationParameter.MULTIPLY'
+ }
+ merge_attrs_mock.return_value = {
+ **params,
+ 'test': 54,
+ 'test2': 'test3'
+ }
+
+ fake_pl = FakeCorrProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ CorrelationFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "Correlation",
+ 'pad': 20,
+ 'kernel_size': 1,
+ 'max_displacement': 20,
+ 'stride_1': 1,
+ 'stride_2': 2,
+ 'single_direction': 0,
+ 'do_abs': False,
+ 'correlation_type': 'caffe.CorrelationParameter.MULTIPLY',
+ 'infer': CorrelationOp.corr_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/ctcgreedydecoder_ext_test.py b/model-optimizer/extensions/front/caffe/ctcgreedydecoder_ext_test.py
new file mode 100644
index 000000000..07b724ed0
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/ctcgreedydecoder_ext_test.py
@@ -0,0 +1,61 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.ctcgreedydecoder_ext import CTCGreedyDecoderFrontExtractor
+from extensions.ops.ctc_greedy_decoder import CTCGreedyDecoderOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeCTCGreedyDecoderProtoLayer:
+ def __init__(self, val):
+ self.ctc_decoder_param = val
+
+
+class TestCTCGreedyDecoderExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['CTCGreedyDecoder'] = CTCGreedyDecoderOp
+
+ def test_ctcgreedydecoder_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, CTCGreedyDecoderFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.ctcgreedydecoder_ext.merge_attrs')
+ def test_ctcgreedydecoder_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'ctc_merge_repeated': True
+ }
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakeCTCGreedyDecoderProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ CTCGreedyDecoderFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "CTCGreedyDecoder",
+ 'ctc_merge_repeated': 1,
+ 'infer': CTCGreedyDecoderOp.ctc_greedy_decoder_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/data_augmentation_ext_test.py b/model-optimizer/extensions/front/caffe/data_augmentation_ext_test.py
new file mode 100644
index 000000000..0dd0abadf
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/data_augmentation_ext_test.py
@@ -0,0 +1,94 @@
+"""
+ Copyright (c) 2017-2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+import numpy as np
+
+from extensions.front.caffe.data_augmentation_ext import DataAugmentationFrontExtractor
+from extensions.ops.data_augmentation import DataAugmentationOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeDAProtoLayer:
+ def __init__(self, val):
+ self.augmentation_param = val
+
+
+class TestDA(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['DataAugmentation'] = DataAugmentationOp
+
+ def test_da_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, DataAugmentationFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.data_augmentation_ext.merge_attrs')
+ def test_da_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'crop_width': 0,
+ 'crop_height': 0,
+ 'write_augmented': "",
+ 'max_multiplier': 255.0,
+ 'augment_during_test': True,
+ 'recompute_mean': 0,
+ 'write_mean': "",
+ 'mean_per_pixel': False,
+ 'mean': 0,
+ 'mode': "add",
+ 'bottomwidth': 0,
+ 'bottomheight': 0,
+ 'num': 0,
+ 'chromatic_eigvec': [0.0]
+
+ }
+ merge_attrs_mock.return_value = {
+ **params,
+ 'test': 54,
+ 'test2': 'test3'
+ }
+ fake_pl = FakeDAProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ DataAugmentationFrontExtractor.extract(fake_node)
+ exp_res = {
+ 'type': 'DataAugmentation',
+ 'op': 'DataAugmentation',
+ 'crop_width': 0,
+ 'crop_height': 0,
+ 'write_augmented': "",
+ 'max_multiplier': 255.0,
+ 'augment_during_test': 1,
+ 'recompute_mean': 0,
+ 'write_mean': "",
+ 'mean_per_pixel': 0,
+ 'mean': 0,
+ 'mode': "add",
+ 'bottomwidth': 0,
+ 'bottomheight': 0,
+ 'num': 0,
+ 'chromatic_eigvec': [0.0],
+ 'infer': DataAugmentationOp.data_augmentation_infer
+ }
+
+ for key in exp_res.keys():
+ if key in ('chromatic_eigvec',):
+ np.testing.assert_equal(exp_res[key], fake_node[key])
+ else:
+ self.assertEqual(exp_res[key], fake_node[key])
diff --git a/model-optimizer/extensions/front/caffe/grn_ext_test.py b/model-optimizer/extensions/front/caffe/grn_ext_test.py
new file mode 100644
index 000000000..e284a8ad7
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/grn_ext_test.py
@@ -0,0 +1,62 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.grn_ext import GRNFrontExtractor
+from extensions.ops.grn import GRNOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.ops.op import Op
+
+
+class FakeGRNProtoLayer:
+ def __init__(self, val):
+ self.grn_param = val
+
+
+class TestGRNExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['GRN'] = GRNOp
+
+ def test_grn_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, GRNFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.grn_ext.merge_attrs')
+ def test_grn_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'bias': 0.7
+ }
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakeGRNProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ GRNFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "GRN",
+ 'bias': 0.7,
+ 'infer': copy_shape_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/interp_ext_test.py b/model-optimizer/extensions/front/caffe/interp_ext_test.py
new file mode 100644
index 000000000..ecbf11401
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/interp_ext_test.py
@@ -0,0 +1,72 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.interp_ext import InterpFrontExtractor
+from extensions.ops.interp import InterpOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeInterpProtoLayer:
+ def __init__(self, val):
+ self.interp_param = val
+
+
+class TestInterpExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Interp'] = InterpOp
+
+ def test_interp_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, InterpFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.interp_ext.merge_attrs')
+ def test_interp_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'height': 1.1,
+ 'width': 2.2,
+ 'zoom_factor': 3.3,
+ 'shrink_factor': 4.4,
+ 'pad_beg': 5.5,
+ 'pad_end': 6.6
+ }
+ merge_attrs_mock.return_value = {
+ **params,
+ 'test': 54,
+ 'test2': 'test3'
+ }
+
+ fake_pl = FakeInterpProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+ InterpFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "Interp",
+ 'height': 1.1,
+ 'width': 2.2,
+ 'zoom_factor': 3.3,
+ 'shrink_factor': 4.4,
+ 'pad_beg': 5.5,
+ 'pad_end': 6.6,
+ 'infer': InterpOp.interp_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/normalize_ext_test.py b/model-optimizer/extensions/front/caffe/normalize_ext_test.py
new file mode 100644
index 000000000..4b2c42fd9
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/normalize_ext_test.py
@@ -0,0 +1,66 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.normalize_ext import NormalizeFrontExtractor
+from extensions.ops.normalize import NormalizeOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.ops.op import Op
+
+
+class FakeNormalizeProtoLayer:
+ def __init__(self, val):
+ self.norm_param = val
+
+
+class TestNormalizeExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Normalize'] = NormalizeOp
+
+ def test_normalize_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, NormalizeFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.normalize_ext.collect_attributes')
+ def test_normalize_ext_ideal_numbers(self, collect_attributes_mock):
+ params = {
+ 'across_spatial': 1,
+ 'channel_shared': 0,
+ 'eps': 0.00001
+ }
+ collect_attributes_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakeNormalizeProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ NormalizeFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "Normalize",
+ 'across_spatial': 1,
+ 'channel_shared': 0,
+ 'eps': 0.00001,
+ 'infer': copy_shape_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/pooling_ext_test.py b/model-optimizer/extensions/front/caffe/pooling_ext_test.py
new file mode 100644
index 000000000..f391d93bd
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/pooling_ext_test.py
@@ -0,0 +1,112 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.front.caffe.pooling_ext import PoolingFrontExtractor
+from mo.front.common.extractors.utils import layout_attrs
+from mo.ops.pooling import Pooling
+from mo.utils.unittest.extractors import PB, FakeMultiParam
+
+
+class FakeProtoLayer:
+ def __init__(self, val):
+ self.pooling_param = val
+
+
+class TestPooling(unittest.TestCase):
+ def test_pooling_ext_global(self):
+ params = {
+ 'kernel_size': 1,
+ 'stride': 2,
+ 'pad': 3,
+ 'pool': 0,
+ 'global_pooling': 1,
+ 'ceil_mode': 1
+ }
+ node = PB({'pb': FakeProtoLayer(FakeMultiParam(params))})
+ PoolingFrontExtractor.extract(node)
+ res = node
+ exp_res = {
+ 'window': np.array([1, 1, 0, 0], dtype=np.int64),
+ 'stride': np.array([1, 1, 1, 1], dtype=np.int64),
+ 'pad': np.array([[0, 0], [0, 0], [0, 0], [0, 0]], dtype=np.int64),
+ 'pad_spatial_shape': np.array([[0, 0], [0, 0]], dtype=np.int64),
+ 'pool_method': 'max',
+ 'exclude_pad': 'true',
+ 'infer': Pooling.infer,
+ 'global_pool': 1,
+ 'output_spatial_shape': None,
+ 'pooling_convention': 'full',
+ 'rounding_type': 'ceil'
+
+ }
+ exp_res.update(layout_attrs())
+ for i in exp_res.keys():
+ if i in ('window', 'stride',
+ 'pad', 'pad_spatial_shape',
+ 'spatial_dims', 'batch_dims',
+ 'channel_dims'):
+ np.testing.assert_array_equal(res[i], exp_res[i])
+ else:
+ self.assertEqual(res[i], exp_res[i])
+
+ def test_pooling_ext(self):
+ params = {
+ 'kernel_size': 1,
+ 'stride': 2,
+ 'pad': 3,
+ 'pool': 1,
+ 'global_pooling': 0,
+ 'ceil_mode': 0
+ }
+ node = PB({'pb': FakeProtoLayer(FakeMultiParam(params))})
+ PoolingFrontExtractor.extract(node)
+ res = node
+ exp_res = {
+ 'window': np.array([1, 1, 1, 1], dtype=np.int64),
+ 'stride': np.array([1, 1, 2, 2], dtype=np.int64),
+ 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]], dtype=np.int64),
+ 'pad_spatial_shape': np.array([[3, 3], [3, 3]], dtype=np.int64),
+ 'pool_method': 'avg',
+ 'exclude_pad': 'false',
+ 'infer': Pooling.infer,
+ 'global_pool': 0,
+ 'output_spatial_shape': None,
+ 'pooling_convention': 'valid'
+ }
+ exp_res.update(layout_attrs())
+ for i in exp_res.keys():
+ if i in ('window', 'stride',
+ 'pad', 'pad_spatial_shape',
+ 'spatial_dims', 'batch_dims',
+ 'channel_dims'):
+ np.testing.assert_array_equal(res[i], exp_res[i])
+ else:
+ self.assertEqual(res[i], exp_res[i])
+
+ def test_pooling_ext_exception(self):
+ params = {
+ 'kernel_size': 1,
+ 'stride': 2,
+ 'pad': 3,
+ 'pool': 3,
+ 'global_pooling': 1
+ }
+ node = PB({'pb': FakeProtoLayer(FakeMultiParam(params))})
+ self.assertRaises(ValueError, PoolingFrontExtractor.extract, node)
diff --git a/model-optimizer/extensions/front/caffe/power_file_ext_test.py b/model-optimizer/extensions/front/caffe/power_file_ext_test.py
new file mode 100644
index 000000000..da06fc66e
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/power_file_ext_test.py
@@ -0,0 +1,64 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.power_file_ext import PowerFileFrontExtractor
+from extensions.ops.power_file import PowerFileOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.ops.op import Op
+
+
+class FakePowerFileProtoLayer:
+ def __init__(self, val):
+ self.power_file_param = val
+
+
+class TestPowerFileExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['PowerFile'] = PowerFileOp
+
+ def test_power_file_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, PowerFileFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.power_file_ext.collect_attributes')
+ def test_mvn_ext_ideal_numbers(self, collect_attributes_mock):
+ params = {
+ 'normalize_variance': 'True',
+ 'across_channels': 'False',
+ 'eps': 1e-9
+ }
+ collect_attributes_mock.return_value = {
+ 'shift_file': 'some_file_path'
+ }
+
+ fake_pl = FakePowerFileProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ PowerFileFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "PowerFile",
+ 'shift_file': 'some_file_path',
+ 'infer': copy_shape_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/prelu_ext_test.py b/model-optimizer/extensions/front/caffe/prelu_ext_test.py
new file mode 100644
index 000000000..2ed4370c5
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/prelu_ext_test.py
@@ -0,0 +1,63 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.prelu_ext import PreluFrontExtractor
+from extensions.ops.prelu import PreluOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakePReLUProtoLayer:
+ def __init__(self, val):
+ self.prelu_param = val
+
+
+class TestPreluExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['PReLU'] = PreluOp
+
+ def test_prelu_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, PreluFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.prelu_ext.merge_attrs')
+ def test_reogyolo_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'channel_shared': False
+ }
+
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakePReLUProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ PreluFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': 'PReLU',
+ 'op': 'PReLU',
+ 'channel_shared': 0,
+ 'infer': PreluOp.prelu_shape_infer,
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/priorbox_clustered_ext_test.py b/model-optimizer/extensions/front/caffe/priorbox_clustered_ext_test.py
new file mode 100644
index 000000000..4ce3e3233
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/priorbox_clustered_ext_test.py
@@ -0,0 +1,88 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+import numpy as np
+
+from extensions.front.caffe.priorbox_clustered_ext import PriorBoxClusteredFrontExtractor
+from extensions.ops.priorbox_clustered import PriorBoxClusteredOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakePriorBoxClusteredProtoLayer:
+ def __init__(self, val):
+ self.prior_box_param = val
+
+
+class TestPriorBoxClusteredExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['PriorBoxClustered'] = PriorBoxClusteredOp
+
+ def test_priorboxclustered_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, PriorBoxClusteredFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.priorbox_clustered_ext.merge_attrs')
+ def test_priorboxclustered_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'width': '30.0',
+ 'height': '60.0',
+ 'clip': False,
+ 'flip': True,
+ 'variance': np.array(['0.2', '0.3', '0.2', '0.3']),
+ 'img_size': '300',
+ 'img_h': '0',
+ 'img_w': '0',
+ 'step': '0,5',
+ 'step_h': '0',
+ 'step_w': '0',
+ 'offset': '0.6'
+ }
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakePriorBoxClusteredProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ PriorBoxClusteredFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'op': 'PriorBoxClustered',
+ 'type': 'PriorBoxClustered',
+ 'width': '30.0',
+ 'height': '60.0',
+ 'clip': 0,
+ 'flip': 1,
+ 'variance': np.array(['0.2', '0.3', '0.2', '0.3']),
+ 'img_size': '300',
+ 'img_h': '0',
+ 'img_w': '0',
+ 'step': '0,5',
+ 'step_h': '0',
+ 'step_w': '0',
+ 'offset': '0.6'
+ }
+
+ for key in exp_res.keys():
+ if key in ['width', 'height', 'variance']:
+ np.testing.assert_equal(fake_node[key], exp_res[key])
+ else:
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/priorbox_ext_test.py b/model-optimizer/extensions/front/caffe/priorbox_ext_test.py
new file mode 100644
index 000000000..b93a8831f
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/priorbox_ext_test.py
@@ -0,0 +1,86 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+import numpy as np
+
+from extensions.front.caffe.priorbox_ext import PriorBoxFrontExtractor
+from extensions.ops.priorbox import PriorBoxOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakePriorBoxProtoLayer:
+ def __init__(self, val):
+ self.prior_box_param = val
+
+
+class TestPriorBoxExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['PriorBox'] = PriorBoxOp
+
+ def test_priorbox_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, PriorBoxFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.priorbox_ext.merge_attrs')
+ def test_priorbox_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'clip': False,
+ 'flip': True,
+ 'min_size': np.array([]),
+ 'max_size': np.array([]),
+ 'aspect_ratio': np.array([2, 3]),
+ 'variance': np.array(['0.2', '0.3', '0.2', '0.3']),
+ 'img_size': '300',
+ 'img_h': '0',
+ 'img_w': '0',
+ 'step': '0,5',
+ 'step_h': '0',
+ 'step_w': '0',
+ 'offset': '0.6'
+ }
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakePriorBoxProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ PriorBoxFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'op': 'PriorBox',
+ 'type': 'PriorBox',
+ 'clip': 0,
+ 'variance': np.array(['0.2', '0.3', '0.2', '0.3']),
+ 'img_size': '300',
+ 'img_h': '0',
+ 'img_w': '0',
+ 'step': '0,5',
+ 'step_h': '0',
+ 'step_w': '0',
+ 'offset': '0.6'
+ }
+
+ for key in exp_res.keys():
+ if key in ['width', 'height', 'variance']:
+ np.testing.assert_equal(fake_node[key], exp_res[key])
+ else:
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/proposal_ext_test.py b/model-optimizer/extensions/front/caffe/proposal_ext_test.py
new file mode 100644
index 000000000..ff41fb0ab
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/proposal_ext_test.py
@@ -0,0 +1,75 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.proposal_ext import ProposalFrontExtractor
+from extensions.ops.proposal import ProposalOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeProposalProtoLayer:
+ def __init__(self, val):
+ self.proposal_param = val
+
+
+class TestProposalExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Proposal'] = ProposalOp
+
+ def test_proposal_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, ProposalFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.proposal_ext.merge_attrs')
+ def test_proposal_ext_ideal_numbers(self, merge_attrs):
+ params = {
+ 'feat_stride': 1,
+ 'base_size': 16,
+ 'min_size': 16,
+ 'ratio': 1,
+ 'scale': 2,
+ 'pre_nms_topn': 6000,
+ 'post_nms_topn': 300,
+ 'nms_thresh': 0.7
+ }
+ merge_attrs.return_value = {
+ **params
+ }
+
+ fake_pl = FakeProposalProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ ProposalFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "Proposal",
+ 'feat_stride': 1,
+ 'base_size': 16,
+ 'min_size': 16,
+ 'ratio': 1,
+ 'scale': 2,
+ 'pre_nms_topn': 6000,
+ 'post_nms_topn': 300,
+ 'nms_thresh': 0.7,
+ 'infer': ProposalOp.proposal_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/proposal_python_ext_test.py b/model-optimizer/extensions/front/caffe/proposal_python_ext_test.py
new file mode 100644
index 000000000..d47f2b76a
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/proposal_python_ext_test.py
@@ -0,0 +1,122 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.proposal_python_ext import ProposalPythonFrontExtractor
+from extensions.ops.proposal import ProposalOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeProposalPythonProtoLayer:
+ def __init__(self, val):
+ self.python_param = val
+
+
+class TestProposalPythonExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Proposal'] = ProposalOp
+
+ def test_proposal_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, ProposalPythonFrontExtractor.extract, None)
+
+ @patch('mo.front.caffe.extractors.utils.merge_attrs')
+ def test_proposal_ext_ideal_numbers(self, merge_attrs):
+ params = {
+ 'param_str': "'feat_stride': 16"
+ }
+ merge_attrs.return_value = params
+
+ fake_pl = FakeProposalPythonProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ ProposalPythonFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "Proposal",
+ 'feat_stride': 16,
+ 'base_size': 16,
+ 'min_size': 16,
+ 'ratio': [0.5, 1, 2],
+ 'scale': [8, 16, 32],
+ 'pre_nms_topn': 6000,
+ 'post_nms_topn': 300,
+ 'nms_thresh': 0.7,
+ 'infer': ProposalOp.proposal_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
+
+ @patch('mo.front.caffe.extractors.utils.merge_attrs')
+ def test_proposal_ext_scales(self, merge_attrs):
+ params = {
+ 'param_str': "'feat_stride': 16, 'scales': [1,2,3], 'ratios':[5, 6,7]"
+ }
+ merge_attrs.return_value = params
+
+ fake_pl = FakeProposalPythonProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ ProposalPythonFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "Proposal",
+ 'feat_stride': 16,
+ 'base_size': 16,
+ 'min_size': 16,
+ 'ratio': [5, 6, 7],
+ 'scale': [1, 2, 3],
+ 'pre_nms_topn': 6000,
+ 'post_nms_topn': 300,
+ 'nms_thresh': 0.7,
+ 'infer': ProposalOp.proposal_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
+
+ @patch('mo.front.caffe.extractors.utils.merge_attrs')
+ def test_proposal_ext_scale(self, merge_attrs):
+ params = {
+ 'param_str': "'feat_stride': 16, 'scale': [1,2,3], 'ratio':[5, 6,7]"
+ }
+ merge_attrs.return_value = params
+
+ fake_pl = FakeProposalPythonProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ ProposalPythonFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "Proposal",
+ 'feat_stride': 16,
+ 'base_size': 16,
+ 'min_size': 16,
+ 'ratio': [5, 6, 7],
+ 'scale': [1, 2, 3],
+ 'pre_nms_topn': 6000,
+ 'post_nms_topn': 300,
+ 'nms_thresh': 0.7,
+ 'infer': ProposalOp.proposal_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/psroipooling_ext_test.py b/model-optimizer/extensions/front/caffe/psroipooling_ext_test.py
new file mode 100644
index 000000000..f1752788b
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/psroipooling_ext_test.py
@@ -0,0 +1,65 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.psroipooling_ext import PSROIPoolingFrontExtractor
+from extensions.ops.psroipooling import PSROIPoolingOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakePSROIPoolingProtoLayer:
+ def __init__(self, val):
+ self.psroi_pooling_param = val
+
+
+class TestPSROIPoolingExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['PSROIPooling'] = PSROIPoolingOp
+
+ def test_psroipooling_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, PSROIPoolingFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.psroipooling_ext.merge_attrs')
+ def test_psroipooling_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'spatial_scale': 4,
+ 'output_dim': 20,
+ 'group_size': 5,
+ }
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakePSROIPoolingProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ PSROIPoolingFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "PSROIPooling",
+ 'spatial_scale': 4,
+ 'output_dim': 20,
+ 'group_size': 5,
+ 'infer': PSROIPoolingOp.psroipooling_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/regionyolo_ext_test.py b/model-optimizer/extensions/front/caffe/regionyolo_ext_test.py
new file mode 100644
index 000000000..8c37989a3
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/regionyolo_ext_test.py
@@ -0,0 +1,79 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.regionyolo_ext import RegionYoloFrontExtractor
+from extensions.ops.regionyolo import RegionYoloOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeRegionYoloProtoLayer:
+ def __init__(self, val, val_f):
+ self.region_yolo_param = val
+ self.flatten_param = val_f
+
+
+class TestReorgYoloExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['RegionYolo'] = RegionYoloOp
+
+ def test_reogyolo_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, RegionYoloFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.regionyolo_ext.merge_attrs')
+ def test_reogyolo_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'coords': 4,
+ 'classes': 20,
+ 'num': 5,
+ 'do_softmax': 1,
+ 'anchors': 5,
+ 'mask': 5,
+ }
+ params_flatten = {
+ 'axis': 1,
+ 'end_axis': -1
+ }
+ merge_attrs_mock.return_value = {
+ **params,
+ **params_flatten
+ }
+
+ fake_pl = FakeRegionYoloProtoLayer(FakeMultiParam(params), FakeMultiParam(params_flatten))
+ fake_node = FakeNode(fake_pl, None)
+
+ RegionYoloFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "RegionYolo",
+ 'coords': 4,
+ 'classes': 20,
+ 'num': 5,
+ 'axis': 1,
+ 'end_axis': -1,
+ 'do_softmax': 1,
+ 'anchors': 5,
+ 'mask': 5,
+ 'infer': RegionYoloOp.regionyolo_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/reorgyolo_ext_test.py b/model-optimizer/extensions/front/caffe/reorgyolo_ext_test.py
new file mode 100644
index 000000000..502c5ad99
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/reorgyolo_ext_test.py
@@ -0,0 +1,61 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.reorgyolo_ext import ReorgYoloFrontExtractor
+from extensions.ops.reorgyolo import ReorgYoloOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeReorgYoloProtoLayer:
+ def __init__(self, val):
+ self.reorg_yolo_param = val
+
+
+class TestReorgYoloExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['ReorgYolo'] = ReorgYoloOp
+
+ def test_elu_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, ReorgYoloFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.reorgyolo_ext.merge_attrs')
+ def test_elu_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'stride': 2
+ }
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakeReorgYoloProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ ReorgYoloFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "ReorgYolo",
+ 'stride': 2,
+ 'infer': ReorgYoloOp.reorgyolo_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/resample_ext_test.py b/model-optimizer/extensions/front/caffe/resample_ext_test.py
new file mode 100644
index 000000000..c1fc3d6aa
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/resample_ext_test.py
@@ -0,0 +1,72 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.resample_ext import ResampleFrontExtractor
+from extensions.ops.resample import ResampleOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeResampleProtoLayer:
+ def __init__(self, val):
+ self.resample_param = val
+
+
+class TestResampleExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Resample'] = ResampleOp
+
+ def test_da_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, ResampleFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.resample_ext.merge_attrs')
+ def test_resample_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'antialias': True,
+ 'height': 384,
+ 'width': 512,
+ 'type': 2,
+ 'factor': 1.0,
+ }
+ merge_attrs_mock.return_value = {
+ 'antialias': True,
+ 'height': 384,
+ 'width': 512,
+ 'type': 'caffe.ResampleParameter.LINEAR',
+ 'factor': 1.0,
+ }
+ fake_pl = FakeResampleProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ ResampleFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'op': 'Resample',
+ 'antialias': 1,
+ 'height': 384,
+ 'width': 512,
+ 'resample_type': 'caffe.ResampleParameter.LINEAR',
+ 'factor': 1.0,
+ 'infer': ResampleOp.resample_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(exp_res[key], fake_node[key])
diff --git a/model-optimizer/extensions/front/caffe/simplernms_ext_test.py b/model-optimizer/extensions/front/caffe/simplernms_ext_test.py
new file mode 100644
index 000000000..06b298bfc
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/simplernms_ext_test.py
@@ -0,0 +1,74 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.simplernms_ext import SimplerNMSFrontExtractor
+from extensions.ops.simplernms import SimplerNMSOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeSimplerNMSProtoLayer:
+ def __init__(self, val):
+ self.simpler_nms_param = val
+
+
+class TestSimplerNMSExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['SimplerNMS'] = SimplerNMSOp
+
+ def test_simplernms_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, SimplerNMSFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.simplernms_ext.merge_attrs')
+ def test_simplernms_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'cls_threshold': 0.5,
+ 'max_num_proposals': 300,
+ 'iou_threshold': 0.7,
+ 'min_bbox_size': 16,
+ 'feat_stride': 16,
+ 'pre_nms_topn': 6000,
+ 'post_nms_topn': 150,
+ 'scale': [1, 2, 3]
+ }
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakeSimplerNMSProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ SimplerNMSFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'cls_threshold': 0.5,
+ 'max_num_proposals': 300,
+ 'iou_threshold': 0.7,
+ 'min_bbox_size': 16,
+ 'feat_stride': 16,
+ 'pre_nms_topn': 6000,
+ 'post_nms_topn': 150,
+ 'scale': [1, 2, 3],
+ 'infer': SimplerNMSOp.simplernms_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/caffe/spatial_transformer_ext_test.py b/model-optimizer/extensions/front/caffe/spatial_transformer_ext_test.py
new file mode 100644
index 000000000..9039cda91
--- /dev/null
+++ b/model-optimizer/extensions/front/caffe/spatial_transformer_ext_test.py
@@ -0,0 +1,81 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from extensions.front.caffe.spatial_transformer_ext import SpatialTransformFrontExtractor
+from extensions.ops.spatial_transformer import SpatialTransformOp
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+from mo.ops.op import Op
+
+
+class FakeSpatialTransformProtoLayer:
+ def __init__(self, val):
+ self.st_param = val
+
+
+class TestSpatialTransformExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['SpatialTransformer'] = SpatialTransformOp
+
+ def test_st_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, SpatialTransformFrontExtractor.extract, None)
+
+ @patch('extensions.front.caffe.spatial_transformer_ext.merge_attrs')
+ def test_st_ext_ideal_numbers(self, merge_attrs_mock):
+ params = {
+ 'transform_type': "ffff",
+ 'sampler_type': "gggg",
+ 'output_H': 56,
+ 'output_W': 78,
+ 'to_compute_dU': True,
+ 'theta_1_1': 0.1,
+ 'theta_1_2': 0.2,
+ 'theta_1_3': 0.3,
+ 'theta_2_1': 0.4,
+ 'theta_2_2': 0.5,
+ 'theta_2_3': 0.6
+ }
+ merge_attrs_mock.return_value = {
+ **params
+ }
+
+ fake_pl = FakeSpatialTransformProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ SpatialTransformFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': "SpatialTransformer",
+ 'transform_type': "ffff",
+ 'sampler_type': "gggg",
+ 'output_H': 56,
+ 'output_W': 78,
+ 'to_compute_dU': 1,
+ 'theta_1_1': 0.1,
+ 'theta_1_2': 0.2,
+ 'theta_1_3': 0.3,
+ 'theta_2_1': 0.4,
+ 'theta_2_2': 0.5,
+ 'theta_2_3': 0.6,
+ 'infer': SpatialTransformOp.sp_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(fake_node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/eltwise_n_test.py b/model-optimizer/extensions/front/eltwise_n_test.py
new file mode 100644
index 000000000..33cedbd03
--- /dev/null
+++ b/model-optimizer/extensions/front/eltwise_n_test.py
@@ -0,0 +1,89 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.eltwise_n import EltwiseNReplacement
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class TestAddNFrontReplacement(unittest.TestCase):
+ def test_replase_eltwise_n(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'add_n': {'value': None, 'operation': 'sum', 'type': None, 'kind': 'op', 'op': 'EltwiseN'},
+ 'node_4': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('node_1', 'node_2'),
+ ('node_2', 'add_n'),
+ ('node_3', 'add_n'),
+ ('add_n', 'node_4'), ],
+ )
+
+ add_n_node = Node(graph, 'add_n')
+ rep_op = EltwiseNReplacement()
+ rep_op.replace_op(graph, add_n_node)
+ eltwise_nodes = [node for node, attrs in list(graph.nodes(data=True)) if attrs['type'] == 'Eltwise']
+ self.assertEqual(len(eltwise_nodes), 1)
+
+ def test_replase_eltwise_n_2(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_4': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'add_n': {'value': None, 'operation': 'sum', 'type': None, 'kind': 'op', 'op': 'EltwiseN'},
+ 'node_5': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('node_1', 'node_2'),
+ ('node_2', 'add_n'),
+ ('node_3', 'add_n'),
+ ('node_4', 'add_n'),
+ ('add_n', 'node_5'), ],
+ )
+
+ add_n_node = Node(graph, 'add_n')
+ rep_op = EltwiseNReplacement()
+ rep_op.replace_op(graph, add_n_node)
+ eltwise_nodes = [node for node, attrs in list(graph.nodes(data=True)) if attrs['type'] == 'Eltwise']
+ self.assertEqual(len(eltwise_nodes), 2)
+
+ def test_replase_eltwise_n_3(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_4': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_5': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'add_n': {'value': None, 'operation': 'sum', 'type': None, 'kind': 'op', 'op': 'EltwiseN'},
+ 'node_6': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('node_1', 'node_2'),
+ ('node_2', 'add_n'),
+ ('node_3', 'add_n'),
+ ('node_4', 'add_n'),
+ ('node_5', 'add_n'),
+ ('add_n', 'node_6'), ],
+ )
+
+ add_n_node = Node(graph, 'add_n')
+ rep_op = EltwiseNReplacement()
+ rep_op.replace_op(graph, add_n_node)
+ eltwise_nodes = [node for node, attrs in list(graph.nodes(data=True)) if attrs['type'] == 'Eltwise']
+ self.assertEqual(len(eltwise_nodes), 3)
diff --git a/model-optimizer/extensions/front/freeze_placeholder_value_test.py b/model-optimizer/extensions/front/freeze_placeholder_value_test.py
new file mode 100644
index 000000000..5c2329148
--- /dev/null
+++ b/model-optimizer/extensions/front/freeze_placeholder_value_test.py
@@ -0,0 +1,103 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.front.freeze_placeholder_value import FreezePlaceholderValue
+from mo.utils.unittest.graph import build_graph
+
+nodes_bool = {
+ '0': {'name': 'input1', 'kind': 'op', 'op': 'Placeholder', 'data_type': bool, 'shape': np.array([])},
+ '1': {'name': 'input2', 'kind': 'op', 'op': 'Placeholder', 'data_type': bool, 'shape': np.array([])},
+ '2': {'name': 'node_1', 'kind': 'op', 'op': 'NotPlaceholder'},
+ '3': {'name': 'node_2', 'kind': 'op', 'op': 'NotPlaceholder'},
+ '4': {'name': 'node_3', 'kind': 'op', 'op': 'NotPlaceholder'},
+ '5': {'name': 'node_4', 'kind': 'op', 'op': 'NotPlaceholder'},
+ '6': {'name': 'output1', 'kind': 'op', 'op': 'OpOutput', 'is_output': True},
+ '7': {'name': 'output2', 'kind': 'op', 'op': 'OpOutput', 'is_output': True}
+
+}
+edges = {
+ ('0', '2'),
+ ('2', '3'),
+ ('4', '6'),
+ ('1', '5'),
+ ('5', '7')
+}
+
+
+class TestFreezePlaceholderValue(unittest.TestCase):
+ def test_freeze_true(self):
+ graph = build_graph(nodes_bool, edges)
+ graph.graph['fw'] = 'tf'
+ tested_class = FreezePlaceholderValue()
+ tested_class.replacement_dict = {'input1': 'True'}
+ before_pattern = graph.nodes()
+ tested_class.find_and_replace_pattern(graph=graph)
+ after_pattern = graph.nodes()
+ # number of nodes in the grpaph didn't change
+ self.assertEqual(len(before_pattern), len(after_pattern))
+ # reach new placeholder
+ try:
+ new_ph_dict = graph.node[[u for u, v in graph.in_edges('2')][0]]
+ except Exception as e:
+ self.fail("Can't get frozen placeholder. Broken edge. Additional information: {}".format(e))
+ # check value
+ self.assertEqual('value' in new_ph_dict, True)
+ self.assertEqual(new_ph_dict['value'], True)
+
+ def test_freeze_false(self):
+ graph = build_graph(nodes_bool, edges)
+ graph.graph['fw'] = 'tf'
+ tested_class = FreezePlaceholderValue()
+ tested_class.replacement_dict = {'input1': 'False'}
+ before_pattern = graph.nodes()
+ tested_class.find_and_replace_pattern(graph=graph)
+ after_pattern = graph.nodes()
+ # number of nodes in the grpaph didn't change
+ self.assertEqual(len(before_pattern), len(after_pattern))
+ # reach new placeholder
+ try:
+ new_ph_dict = graph.node[[u for u, v in graph.in_edges('2')][0]]
+ except Exception as e:
+ self.fail("Can't get frozen placeholder. Broken edge. Additional information: {}".format(e))
+ # check value
+ self.assertEqual('value' in new_ph_dict, True)
+ self.assertEqual(new_ph_dict['value'], False)
+
+ def test_freeze_both(self):
+ graph = build_graph(nodes_bool, edges)
+ graph.graph['fw'] = 'tf'
+ tested_class = FreezePlaceholderValue()
+ tested_class.replacement_dict = {'input1': 'False', 'input2': 'True'}
+ before_pattern = graph.nodes()
+ tested_class.find_and_replace_pattern(graph=graph)
+ after_pattern = graph.nodes()
+ # number of nodes in the graph didn't change
+ self.assertEqual(len(before_pattern), len(after_pattern))
+ # reach new placeholder
+ try:
+ new_ph_dict_1 = graph.node[[u for u, v in graph.in_edges('2')][0]]
+ new_ph_dict_2 = graph.node[[u for u, v in graph.in_edges('5')][0]]
+ except Exception as e:
+ self.fail("Can't get frozen placeholder. Broken edge. Additional information: {}".format(e))
+ # check value
+ self.assertEqual('value' in new_ph_dict_1, True)
+ self.assertEqual('value' in new_ph_dict_2, True)
+ self.assertEqual(new_ph_dict_1['value'], False)
+ self.assertEqual(new_ph_dict_2['value'], True)
diff --git a/model-optimizer/extensions/front/image_scaler_test.py b/model-optimizer/extensions/front/image_scaler_test.py
new file mode 100644
index 000000000..2c4ec9085
--- /dev/null
+++ b/model-optimizer/extensions/front/image_scaler_test.py
@@ -0,0 +1,103 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.front.image_scaler import ImageScaler
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ImageScaler operation
+ 'im_scaler': {'type': None, 'kind': 'op', 'op': 'ImageScaler'},
+ 'im_scaler_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Test operation
+ 'last': {'type': None, 'value': None, 'kind': 'op', 'op': None},
+ 'last_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Mul and Add operations
+ 'mul_1': {'type': None, 'value': None, 'kind': 'op', 'op': 'Mul'},
+ 'mul_1_w': {'value': None, 'shape': None, 'kind': 'op', 'op': 'Const'},
+ 'mul_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'add_1': {'type': None, 'value': None, 'kind': 'op', 'op': 'Add'},
+ 'add_1_w': {'value': None, 'shape': None, 'kind': 'op', 'op': 'Const'},
+ 'add_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class ImageScalerTest(unittest.TestCase):
+ def test_image_scaler_test1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'im_scaler'),
+ ('im_scaler', 'im_scaler_data'),
+ ('im_scaler_data', 'last'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'im_scaler': {'scale': np.array(1.0), 'bias': np.reshape(np.array([1, 2, 3]), [3, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'last')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_w': {'shape': np.array([3, 1, 1]),
+ 'value': np.reshape(np.array([1, 2, 3]), [3, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+
+ replacer = ImageScaler()
+ replacer.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'last')
+ self.assertTrue(flag, resp)
+
+ def test_image_scaler_test2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'im_scaler'),
+ ('im_scaler', 'im_scaler_data'),
+ ('im_scaler_data', 'last'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'im_scaler': {'scale': np.array(2.0), 'bias': np.reshape(np.array([0, 0, 0]), [3, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'last')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array(2.0).shape, 'value': np.array(2.0)},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+
+ replacer = ImageScaler()
+ replacer.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'last')
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/front/instance_normalization_test.py b/model-optimizer/extensions/front/instance_normalization_test.py
new file mode 100644
index 000000000..90dbe1bff
--- /dev/null
+++ b/model-optimizer/extensions/front/instance_normalization_test.py
@@ -0,0 +1,61 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import networkx as nx
+
+from extensions.front.instance_normalization import InstanceNormalization
+from mo.utils.unittest.graph import build_graph
+from mo.middle.pattern_match import node_match
+
+
+class TestInstanceNormalization(unittest.TestCase):
+ def test_default(self):
+ nodes = {
+ 'input': {'kind': 'op', 'op': 'AnyOp'},
+ 'scale': {'kind': 'op', 'op': 'AnyOp'},
+ 'B': {'kind': 'op', 'op': 'AnyOp'},
+ 'node': {'kind': 'op', 'op': 'InstanceNormalization', 'epsilon': 0.123},
+ }
+ edges = [
+ ('input', 'node'),
+ ('scale', 'node'),
+ ('B', 'node'),
+ ]
+
+ graph = build_graph(nodes, edges)
+ tested_class = InstanceNormalization()
+ tested_class.find_and_replace_pattern(graph)
+
+ ref_nodes = {
+ 'input': {'op': 'AnyOp'},
+ 'scale': {'op': 'AnyOp'},
+ 'B': {'op': 'AnyOp'},
+ 'mvn': {'kind': 'op', 'op': 'MVN', 'name': 'node/InstanceNormalization/MVN', 'eps': 0.123},
+ 'mul': {'kind': 'op', 'op': 'Mul', 'name': 'node/InstanceNormalization/Mul'},
+ 'add': {'kind': 'op', 'op': 'Add', 'name': 'node/InstanceNormalization/Add'},
+ }
+ ref_edges = [
+ ('input', 'mvn'),
+ ('mvn', 'mul'),
+ ('scale', 'mul'),
+ ('mul', 'add'),
+ ('B', 'add'),
+ ]
+
+ ref_graph = build_graph(ref_nodes, ref_edges)
+ self.assertTrue(nx.is_isomorphic(graph, ref_graph, node_match))
diff --git a/model-optimizer/extensions/front/kaldi/replace_splice_node_pattern_test.py b/model-optimizer/extensions/front/kaldi/replace_splice_node_pattern_test.py
new file mode 100644
index 000000000..f967f4b0d
--- /dev/null
+++ b/model-optimizer/extensions/front/kaldi/replace_splice_node_pattern_test.py
@@ -0,0 +1,57 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+from extensions.front.kaldi.replace_splice_node_pattern import ReplaceSpliceNodePattern
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+
+class ReplaceSpliceNodePatternTests(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls.nodes_attributes = {
+ 'in_node': {'kind': 'op', 'op': 'Input', 'shape': [1, 13]},
+ 'slice': {'kind': 'op', 'op': 'Splice', 'context': range(-5, 5)}
+ }
+ cls.graph = build_graph(cls.nodes_attributes,
+ [('in_node', 'slice')])
+
+ ReplaceSpliceNodePattern().find_and_replace_pattern(cls.graph)
+
+ def test_memory(self):
+ memory_nodes = [node for node in self.graph.nodes(data=True) if node[1]['op'] == 'Memory']
+ self.assertEqual(len(memory_nodes), 2)
+ for memory_node in memory_nodes:
+ node = Node(self.graph, memory_node[0])
+ if len(node.in_nodes()):
+ self.assertEqual(node.index, 0)
+ elif len(node.out_nodes()):
+ self.assertEqual(node.index, 1)
+ self.assertEqual(memory_nodes[0][1]['id'], memory_nodes[1][1]['id'])
+
+ def test_crop(self):
+ crop_node = [node for node in self.graph.nodes(data=True) if node[1]['op'] == 'Crop']
+ self.assertEqual(len(crop_node), 1)
+ crop_node = Node(self.graph, crop_node[0][0])
+ self.assertEqual(crop_node.offset, [13])
+ self.assertEqual(crop_node.dim, [13 * 9])
+
+ def test_concat(self):
+ concat_node = [node for node in self.graph.nodes(data=True) if node[1]['op'] == 'Concat']
+ self.assertEqual(len(concat_node), 1)
+ crop_node = Node(self.graph, concat_node[0][0])
+ self.assertEqual(crop_node.axis, 1)
diff --git a/model-optimizer/extensions/front/mxnet/check_softmax_node_inputs_test.py b/model-optimizer/extensions/front/mxnet/check_softmax_node_inputs_test.py
new file mode 100644
index 000000000..ea7da2a4b
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/check_softmax_node_inputs_test.py
@@ -0,0 +1,60 @@
+"""
+ Copyright (c) 2017-2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.mxnet.check_softmax_node_inputs import CheckSoftmaxNodeInputs
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class TestCheckSoftmaxNodeInputs(unittest.TestCase):
+ def test_remove_softmax_output_input(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'softmax': {'type': 'SoftmaxOutput', 'value': None, 'kind': 'op', 'op': 'SoftmaxOutput'},
+ },
+ [('node_1', 'softmax'),
+ ('node_2', 'softmax')
+ ])
+
+ pattern = CheckSoftmaxNodeInputs()
+ pattern.find_and_replace_pattern(graph)
+
+ node_softmax = Node(graph, 'softmax')
+
+ self.assertEqual(len(node_softmax.in_nodes()), 1)
+
+ node_input1 = node_softmax.in_node(0)
+ self.assertEqual(node_input1.name, 'node_1')
+
+ def test_remove_softmax_activation_input(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'softmax': {'type': 'SoftmaxActivation', 'value': None, 'kind': 'op', 'op': 'SoftmaxActivation'},
+ },
+ [('node_1', 'softmax')])
+
+ pattern = CheckSoftmaxNodeInputs()
+ pattern.find_and_replace_pattern(graph)
+
+ node_softmax = Node(graph, 'softmax')
+
+ self.assertEqual(len(node_softmax.in_nodes()), 1)
+
+ node_input1 = node_softmax.in_node(0)
+ self.assertEqual(node_input1.name, 'node_1')
diff --git a/model-optimizer/extensions/front/mxnet/conv_ext_test.py b/model-optimizer/extensions/front/mxnet/conv_ext_test.py
new file mode 100644
index 000000000..ee686889f
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/conv_ext_test.py
@@ -0,0 +1,152 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.front.mxnet.conv_ext import DeconvFrontExtractor
+from mo.utils.unittest.extractors import PB
+
+
+class TestDeconvShapesParsing(unittest.TestCase):
+ def test_conv_ext_ideal_numbers(self):
+ params = {'attrs': {
+ "kernel": "(4, 4)",
+ "no_bias": "True",
+ "num_filter": "21",
+ "num_group": "14",
+ "pad": "(4, 4)",
+ "stride": "(2, 2)",
+ "dilate": "(3, 3)",
+ "workspace": "1536"
+ }}
+ node = PB({'symbol_dict': params})
+ DeconvFrontExtractor.extract(node)
+ exp_res = {
+ 'op': 'Deconvolution',
+ 'pad': np.array([[0, 0], [0, 0], [4, 4], [4, 4]]),
+ 'pad_spatial_shape': np.array([[4, 4], [4, 4]]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'kernel_spatial': np.array([4, 4]),
+ 'dilation': np.array([1, 1, 3, 3]),
+ 'group': 14,
+ 'output': 21,
+ 'bias_addable': True,
+ 'bias_term': False,
+ }
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation'):
+ np.testing.assert_equal(node[key], exp_res[key])
+ else:
+ self.assertEqual(node[key], exp_res[key])
+
+
+ def test_conv_ext_no_bias(self):
+ params = { 'attrs':{
+ "kernel": "(4, 4)",
+ "num_filter": "21",
+ "num_group": "14",
+ "pad": "(4, 4)",
+ "stride": "(2, 2)",
+ "dilate": "(3, 3)",
+ "workspace": "1536"
+ }}
+ node = PB({'symbol_dict': params})
+ DeconvFrontExtractor.extract(node)
+ exp_res = {
+ 'op': 'Deconvolution',
+ 'pad': np.array([[0, 0], [0, 0], [4, 4], [4, 4]]),
+ 'pad_spatial_shape': np.array([[4, 4], [4, 4]]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'kernel_spatial': np.array([4, 4]),
+ 'dilation': np.array([1, 1, 3, 3]),
+ 'group': 14,
+ 'output': 21,
+ 'bias_addable': True,
+ 'bias_term': False,
+ }
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation'):
+ np.testing.assert_equal(node[key], exp_res[key])
+ else:
+ self.assertEqual(node[key], exp_res[key])
+
+
+ def test_conv_ext_with_bias(self):
+ params = { 'attrs':{
+ "kernel": "(4, 4)",
+ "no_bias": "False",
+ "num_filter": "21",
+ "num_group": "14",
+ "pad": "(4, 4)",
+ "stride": "(2, 2)",
+ "dilate": "(3, 3)",
+ "workspace": "1536"
+ }}
+ node = PB({'symbol_dict': params})
+ DeconvFrontExtractor.extract(node)
+ exp_res = {
+ 'op': 'Deconvolution',
+ 'pad': np.array([[0, 0], [0, 0], [4, 4], [4, 4]]),
+ 'pad_spatial_shape': np.array([[4, 4], [4, 4]]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'kernel_spatial': np.array([4, 4]),
+ 'dilation': np.array([1, 1, 3, 3]),
+ 'group': 14,
+ 'output': 21,
+ 'bias_addable': True,
+ 'bias_term': True,
+ }
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation'):
+ np.testing.assert_equal(node[key], exp_res[key])
+ else:
+ self.assertEqual(node[key], exp_res[key])
+
+
+ def test_deconv_ext_target_shape(self):
+ params = {'attrs': {
+ "kernel": "(4, 4)",
+ "no_bias": "True",
+ "num_filter": "21",
+ "num_group": "14",
+ "pad": "(4, 4)",
+ "stride": "(2, 2)",
+ "dilate": "(3, 3)",
+ "workspace": "1536",
+ "target_shape": "(120, 120)"
+ }}
+ node = PB({'symbol_dict': params})
+ DeconvFrontExtractor.extract(node)
+ exp_res = {
+ 'op': 'Deconvolution',
+ 'pad': np.array([[0, 0], [0, 0], [4, 4], [4, 4]]),
+ 'pad_spatial_shape': np.array([[4, 4], [4, 4]]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'kernel_spatial': np.array([4, 4]),
+ 'dilation': np.array([1, 1, 3, 3]),
+ 'group': 14,
+ 'output': 21,
+ 'bias_addable': True,
+ 'bias_term': False,
+ 'output_spatial_shape': np.array([120, 120]),
+ }
+ for key in exp_res.keys():
+ if key in ('pad', 'pad_spatial_shape', 'stride', 'kernel_spatial', 'dilation', 'output_spatial_shape'):
+ np.testing.assert_equal(node[key], exp_res[key])
+ else:
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/mxnet/custom_test.py b/model-optimizer/extensions/front/mxnet/custom_test.py
new file mode 100644
index 000000000..3d698cf9a
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/custom_test.py
@@ -0,0 +1,57 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.mxnet.custom import CustomFrontExtractorOp
+from mo.utils.unittest.graph import build_graph
+from mo.front.extractor import FrontExtractorOp, MXNetCustomFrontExtractorOp
+from mo.graph.graph import Node
+
+attrs = {'test_attr': 1}
+
+
+class FakeExtractor(MXNetCustomFrontExtractorOp):
+ @staticmethod
+ def extract(node: Node):
+ return True, attrs
+
+
+class TestCustomFrontExtractorOp(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ FrontExtractorOp.registered_ops['Custom'] = CustomFrontExtractorOp
+
+ def test_extract_custom_layer(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_custom': {'type': 'Custom', 'value': None, 'kind': 'op', 'op': 'Custom', },
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('node_1', 'node_2'),
+ ('node_2', 'node_custom'),
+ ('node_custom', 'node_3'),
+ ],
+ {
+ 'node_custom': {'symbol_dict': {'attrs': {'op_type': 'test_type'}}},
+ })
+
+ custom_node = Node(graph, 'node_custom')
+ custom_op = FakeExtractor()
+ supported, op_attrs = custom_op.extract(custom_node)
+ self.assertTrue(supported)
+ self.assertEquals(op_attrs, attrs)
diff --git a/model-optimizer/extensions/front/mxnet/pooling_ext_test.py b/model-optimizer/extensions/front/mxnet/pooling_ext_test.py
new file mode 100644
index 000000000..43450a818
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/pooling_ext_test.py
@@ -0,0 +1,50 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.front.mxnet.pooling_ext import PoolingFrontExtractor
+from mo.utils.unittest.extractors import PB
+
+
+class TestPoolingShapesParsing(unittest.TestCase):
+ def test_conv_ext_ideal_numbers(self):
+ params = {'attrs': {
+ "kernel": "(3, 4)",
+ "stride": "(3, 2)",
+ "pad": "(7, 8)",
+ "pool_type": "max"
+ }}
+
+ node = PB({'symbol_dict': params})
+ PoolingFrontExtractor.extract(node)
+ exp_res = {
+ 'op': 'Pooling',
+ 'pad': np.array([[0, 0], [0, 0], [7, 7], [8, 8]]),
+ 'pad_spatial_shape': np.array([[7, 7], [8, 8]]),
+ 'stride': np.array([1, 1, 3, 2]),
+ 'window': np.array([1, 1, 3, 4]),
+ 'pool_method': 'max',
+ 'exclude_pad': 'false',
+ }
+
+ for key in exp_res.keys():
+ if key in ('pad', 'stride', 'window', 'pad_spatial_shape'):
+ np.testing.assert_equal(node[key], exp_res[key])
+ else:
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/mxnet/slice_channel_ext_test.py b/model-optimizer/extensions/front/mxnet/slice_channel_ext_test.py
new file mode 100644
index 000000000..080e87121
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/slice_channel_ext_test.py
@@ -0,0 +1,55 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.mxnet.slice_channel_ext import SliceChannelFrontExtractor
+from mo.utils.unittest.extractors import PB
+
+
+class TestSliceChannelParsing(unittest.TestCase):
+ def test_parse_values(self):
+ params = {'attrs': {
+ "num_outputs": "2",
+ 'axis': "2",
+ }}
+
+ node = PB({'symbol_dict': params})
+ SliceChannelFrontExtractor.extract(node)
+ exp_res = {
+ 'op': 'Split',
+ 'axis': 2,
+ 'num_split': 2,
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(node[key], exp_res[key])
+
+ def test_parse_dafault_values(self):
+ params = {'attrs': {
+ "num_outputs": "2",
+ }}
+
+ node = PB({'symbol_dict': params})
+ SliceChannelFrontExtractor.extract(node)
+ exp_res = {
+ 'op': 'Split',
+ 'axis': 1,
+ 'num_split': 2,
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/mxnet/ssd_pattern_flatten_softmax_activation_test.py b/model-optimizer/extensions/front/mxnet/ssd_pattern_flatten_softmax_activation_test.py
new file mode 100644
index 000000000..7c9bc9ef5
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/ssd_pattern_flatten_softmax_activation_test.py
@@ -0,0 +1,45 @@
+"""
+ Copyright (c) 2017-2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.mxnet.ssd_pattern_flatten_softmax_activation import SsdPatternFlattenSoftmaxActivation
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class TestSsdPatternFlattenSoftmaxActivation(unittest.TestCase):
+ def test_pattern_remove_transpose(self):
+ graph = build_graph({'node_1': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'},
+ 'node_softmax_activation': {'type': 'SoftMax', 'kind': 'op', 'op': 'SoftMax'},
+ 'node_multi_box_detection': {'type': '_contrib_MultiBoxDetection', 'kind': 'op',
+ 'op': '_contrib_MultiBoxDetection'},
+ 'node_4': {'type': 'Identity', 'kind': 'op'},
+ },
+ [('node_1', 'node_softmax_activation'),
+ ('node_2', 'node_multi_box_detection'),
+ ('node_softmax_activation', 'node_multi_box_detection'),
+ ('node_3', 'node_multi_box_detection'),
+ ('node_multi_box_detection', 'node_4'), ],
+ )
+
+ pattern = SsdPatternFlattenSoftmaxActivation()
+ pattern.find_and_replace_pattern(graph)
+ flatten_name = list(graph.nodes())[-1]
+ self.assertTrue(graph.has_node(flatten_name))
+ self.assertFalse(graph.has_edge(Node(graph, 'softmax_activation').id, Node(graph, 'multi_box_detection').id))
diff --git a/model-optimizer/extensions/front/mxnet/ssd_pattern_remove_flatten_test.py b/model-optimizer/extensions/front/mxnet/ssd_pattern_remove_flatten_test.py
new file mode 100644
index 000000000..dfd5708ff
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/ssd_pattern_remove_flatten_test.py
@@ -0,0 +1,42 @@
+"""
+ Copyright (c) 2017-2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.mxnet.ssd_pattern_remove_flatten import SsdPatternRemoveFlatten
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class TestSsdPatternRemoveFlatten(unittest.TestCase):
+ def test_pattern_remove_transpose(self):
+ graph = build_graph({'node_1': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ 'node_multi_box_prior': {'type': '_contrib_MultiBoxPrior', 'kind': 'op',
+ 'op': '_contrib_MultiBoxPrior'},
+ 'node_flatten': {'type': 'Flatten', 'kind': 'op', 'op': 'Flatten'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'},
+ },
+ [('node_1', 'node_2'),
+ ('node_2', 'node_multi_box_prior'),
+ ('node_multi_box_prior', 'node_flatten'),
+ ('node_flatten', 'node_3'), ],
+ )
+
+ pattern = SsdPatternRemoveFlatten()
+ pattern.find_and_replace_pattern(graph)
+ self.assertFalse(graph.has_node('node_flatten'))
+ self.assertTrue(graph.has_edge(Node(graph, 'node_multi_box_prior').id, Node(graph, 'node_3').id))
diff --git a/model-optimizer/extensions/front/mxnet/ssd_pattern_remove_reshape_test.py b/model-optimizer/extensions/front/mxnet/ssd_pattern_remove_reshape_test.py
new file mode 100644
index 000000000..40a76491c
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/ssd_pattern_remove_reshape_test.py
@@ -0,0 +1,56 @@
+"""
+ Copyright (c) 2017-2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.mxnet.ssd_pattern_remove_reshape import SsdPatternRemoveReshape
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class TestSsdPatternRemoveReshape(unittest.TestCase):
+ def test_pattern_remove_reshape(self):
+ graph = build_graph({'node_1': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ 'node_multi_box_prior1': {'type': '_contrib_MultiBoxPrior', 'kind': 'op',
+ 'op': '_contrib_MultiBoxPrior'},
+ 'node_multi_box_prior2': {'type': '_contrib_MultiBoxPrior', 'kind': 'op',
+ 'op': '_contrib_MultiBoxPrior'},
+ 'node_multi_box_prior3': {'type': '_contrib_MultiBoxPrior', 'kind': 'op',
+ 'op': '_contrib_MultiBoxPrior'},
+ 'node_concat': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+ 'node_reshape': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'},
+ },
+ [('node_1', 'node_2'),
+ ('node_2', 'node_multi_box_prior1'),
+ ('node_2', 'node_multi_box_prior2'),
+ ('node_2', 'node_multi_box_prior3'),
+ ('node_multi_box_prior1', 'node_concat'),
+ ('node_multi_box_prior2', 'node_concat'),
+ ('node_multi_box_prior3', 'node_concat'),
+ ('node_concat', 'node_reshape'),
+ ('node_reshape', 'node_3'), ],
+ {
+ 'node_concat': {'symbol_dict': {'attrs': {'dim': 3}}},
+ })
+
+ pattern = SsdPatternRemoveReshape()
+ pattern.find_and_replace_pattern(graph)
+ node_concat = Node(graph, 'node_concat')
+ self.assertEqual(node_concat['symbol_dict']['attrs']['dim'], 2)
+ self.assertFalse(graph.has_node('node_reshape'))
+ self.assertTrue(graph.has_edge(Node(graph, 'node_concat').id, Node(graph, 'node_3').id))
diff --git a/model-optimizer/extensions/front/mxnet/ssd_pattern_remove_transpose_test.py b/model-optimizer/extensions/front/mxnet/ssd_pattern_remove_transpose_test.py
new file mode 100644
index 000000000..576e2f91d
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/ssd_pattern_remove_transpose_test.py
@@ -0,0 +1,47 @@
+"""
+ Copyright (c) 2017-2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.mxnet.ssd_pattern_remove_transpose import SsdPatternRemoveTranspose
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class TestSsdPatternRemoveTranspose(unittest.TestCase):
+ def test_pattern_remove_transpose(self):
+ graph = build_graph({'node_1': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_4': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_transpose': {'type': 'transpose', 'value': None, 'kind': 'op', 'op': 'transpose'},
+ 'node_softmax_activation': {'type': 'SoftMax', 'value': None, 'kind': 'op',
+ 'op': 'SoftMax'},
+ 'node_multi_box_detection': {'type': '_contrib_MultiBoxDetection', 'value': None,
+ 'kind': 'op', 'op': '_contrib_MultiBoxDetection'},
+ 'node_5': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('node_1', 'node_transpose'),
+ ('node_transpose', 'node_softmax_activation'),
+ ('node_3', 'node_multi_box_detection'),
+ ('node_softmax_activation', 'node_multi_box_detection'),
+ ('node_4', 'node_multi_box_detection'),
+ ('node_multi_box_detection', 'node_5'), ],
+ )
+
+ pattern = SsdPatternRemoveTranspose()
+ pattern.find_and_replace_pattern(graph)
+ self.assertFalse(graph.has_node('node_transpose'))
+ self.assertTrue(graph.has_edge(Node(graph, 'node_1').id, Node(graph, 'node_softmax_activation').id))
diff --git a/model-optimizer/extensions/front/mxnet/ssd_reorder_detection_out_inputs_test.py b/model-optimizer/extensions/front/mxnet/ssd_reorder_detection_out_inputs_test.py
new file mode 100644
index 000000000..6ddde4cfa
--- /dev/null
+++ b/model-optimizer/extensions/front/mxnet/ssd_reorder_detection_out_inputs_test.py
@@ -0,0 +1,54 @@
+"""
+ Copyright (c) 2017-2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.front.mxnet.ssd_reorder_detection_out_inputs import SsdReorderDetectionOutInputs
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class TestSsdReorderDetectionOutInputs(unittest.TestCase):
+ def test_reorder_detection_out_inputs(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'node_3': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'multi_box_detection': {'type': '_contrib_MultiBoxDetection', 'kind': 'op',
+ 'op': '_contrib_MultiBoxDetection'},
+ },
+ [('node_1', 'multi_box_detection'),
+ ('node_2', 'multi_box_detection'),
+ ('node_3', 'multi_box_detection')],
+ {
+ 'node_1': {'shape': np.array([1, 34928])},
+ 'node_2': {'shape': np.array([1, 183372])},
+ 'node_3': {'shape': np.array([1, 2, 34928])},
+ })
+
+ pattern = SsdReorderDetectionOutInputs()
+ pattern.find_and_replace_pattern(graph)
+
+ node_multi_box = Node(graph, 'multi_box_detection')
+
+ node_input1 = node_multi_box.in_node(0)
+ node_input2 = node_multi_box.in_node(1)
+ node_input3 = node_multi_box.in_node(2)
+ self.assertEqual(node_input1.name, 'node_2')
+ self.assertEqual(node_input2.name, 'node_1')
+ self.assertEqual(node_input3.name, 'node_3')
diff --git a/model-optimizer/extensions/front/onnx/affine_ext_test.py b/model-optimizer/extensions/front/onnx/affine_ext_test.py
new file mode 100644
index 000000000..799e6433f
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/affine_ext_test.py
@@ -0,0 +1,79 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+import onnx
+
+from extensions.front.onnx.affine_ext import AffineFrontExtractor
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class AffineONNXExtractorTest(unittest.TestCase):
+ @staticmethod
+ def _create_node(attrs: dict):
+ pb = onnx.helper.make_node("Affine", ["X"], ["Y"], **attrs)
+ graph = build_graph({'node_0': {'pb': pb}}, [])
+ return Node(graph, 'node_0')
+
+ @staticmethod
+ def _base_attrs():
+ # Commonly used attributes in the tests
+ # Each test takes these ones and then adds/modifies/deletes particular fields
+ return (
+ # test input ONNX attributes
+ dict(
+ alpha=1.0,
+ beta=0.0
+ ),
+ # reference output Node attributes
+ dict(
+ op='ImageScaler',
+ scale=1.0,
+ bias=0.0
+ )
+ )
+
+ @staticmethod
+ def _extract(inp):
+ node = __class__._create_node(inp)
+ AffineFrontExtractor.extract(node)
+ return node.graph.node[node.id]
+
+ def _match(self, out, ref):
+ for key in ref.keys():
+ status = out[key] == ref[key]
+ if type(status) in [list, np.ndarray]:
+ status = np.all(status)
+ self.assertTrue(status, 'Mismatch for field {}, observed: {}, expected: {}'.format(key, out[key], ref[key]))
+
+ def test_default(self):
+ inp, ref = self._base_attrs()
+ out = self._extract(inp)
+ self._match(out, ref)
+
+ def test_random(self):
+ inp, ref = self._base_attrs()
+ inp['alpha'] = 123.
+ inp['beta'] = 321.
+
+ ref['scale'] = 123.
+ ref['bias'] = 321.
+
+ out = self._extract(inp)
+ self._match(out, ref)
diff --git a/model-optimizer/extensions/front/onnx/conv_ext_test.py b/model-optimizer/extensions/front/onnx/conv_ext_test.py
new file mode 100644
index 000000000..937542a39
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/conv_ext_test.py
@@ -0,0 +1,137 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+import onnx
+
+from extensions.front.onnx.conv_ext import ConvTransposeFrontExtractor
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+from mo.utils.error import Error
+
+
+class ConvTransposeONNXExtractorTest(unittest.TestCase):
+ @staticmethod
+ def _create_node(attrs: dict):
+ pb = onnx.helper.make_node("ConvTranspose", ["X", "W"], ["Y"], **attrs)
+ graph = build_graph({'node_0': {'pb': pb}}, [])
+ return Node(graph, 'node_0')
+
+ @staticmethod
+ def _base_attrs():
+ # Commonly used attributes in the tests
+ # Each test takes these ones and then adds/modifies/deletes particular fields
+ return (
+ # test input ONNX attributes
+ dict(
+ pads=[1, 2, 3, 4],
+ kernel_shape=[5, 6]
+ ),
+ # reference output Node attributes
+ dict(
+ type='Deconvolution',
+ pad=[[0, 0], [0, 0], [1, 3], [2, 4]],
+ pad_spatial_shape=[[1, 3], [2, 4]],
+ kernel_spatial=[5, 6],
+ bias_term=None,
+ output_shape=None,
+ output_padding=[0, 0, 0, 0],
+ dilation=[1, 1, 1, 1],
+ stride=[1, 1, 1, 1],
+ output_spatial_shape=None,
+ group=1
+ )
+ )
+
+ @staticmethod
+ def _extract(inp):
+ node = __class__._create_node(inp)
+ ConvTransposeFrontExtractor.extract(node)
+ return node.graph.node[node.id]
+
+ def _match(self, out, ref):
+ for key in ref.keys():
+ status = out[key] == ref[key]
+ if type(status) in [list, np.ndarray]:
+ status = np.all(status)
+ self.assertTrue(status, 'Mismatch for field {}, observed: {}, expected: {}'.format(key, out[key], ref[key]))
+
+ def test_all_valid_default(self):
+ inp, ref = self._base_attrs()
+ del inp['pads']
+ ref['pad'] = [[0, 0], [0, 0], [0, 0], [0, 0]]
+ ref['pad_spatial_shape'] = [[0, 0], [0, 0]]
+ out = self._extract(inp)
+ self._match(out, ref)
+
+ def test_most_used(self):
+ inp, ref = self._base_attrs()
+ out = self._extract(inp)
+ self._match(out, ref)
+
+ def test_dilation(self):
+ inp, ref = self._base_attrs()
+ inp['dilations'] = [10, 11]
+ ref['dilation'] = [1, 1, 10, 11]
+ out = self._extract(inp)
+ self._match(out, ref)
+
+ def test_stride(self):
+ inp, ref = self._base_attrs()
+ inp['strides'] = [12, 13]
+ ref['stride'] = [1, 1, 12, 13]
+ out = self._extract(inp)
+ self._match(out, ref)
+
+ def test_group(self):
+ inp, ref = self._base_attrs()
+ inp['group'] = 14
+ ref['group'] = 14
+ out = self._extract(inp)
+ self._match(out, ref)
+
+ def test_auto_pad_supported(self):
+ inp, ref = self._base_attrs()
+ del inp['pads']
+ inp['auto_pad'] = 'SAME_UPPER'
+
+ ref['auto_pad'] = 'same_upper'
+ ref['pad'] = [[0, 0], [0, 0], [0, 0], [0, 0]]
+ ref['pad_spatial_shape'] = [[0, 0], [0, 0]]
+
+ out = self._extract(inp)
+ self._match(out, ref)
+
+ def test_pads_not_even_invalid(self):
+ inp, ref = self._base_attrs()
+ inp['pads'] = [1, 2, 3]
+ with self.assertRaisesRegex(Error, '.*pads.*not correct.*'):
+ out = self._extract(inp)
+
+ def test_missing_kernel_shape_not_supported(self):
+ inp, ref = self._base_attrs()
+ del inp['kernel_shape']
+ with self.assertRaisesRegex(Error, '.*kernel_shape.*not supported.*'):
+ out = self._extract(inp)
+
+ def test_output_padding(self):
+ inp, ref = self._base_attrs()
+ inp['output_padding'] = [19, 20]
+ ref['output_padding'] = [0, 0, 19, 20]
+ out = self._extract(inp)
+ self._match(out, ref)
diff --git a/model-optimizer/extensions/front/onnx/crop_ext_test.py b/model-optimizer/extensions/front/onnx/crop_ext_test.py
new file mode 100644
index 000000000..1696b69a5
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/crop_ext_test.py
@@ -0,0 +1,80 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+import onnx
+
+from extensions.front.onnx.crop_ext import CropFrontExtractor
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class CropONNXExtractorTest(unittest.TestCase):
+ @staticmethod
+ def _create_node(attrs: dict):
+ pb = onnx.helper.make_node("Crop", ["X"], ["Y"], **attrs)
+ graph = build_graph({'node_0': {'pb': pb}}, [])
+ return Node(graph, 'node_0')
+
+ @staticmethod
+ def _base_attrs():
+ # Commonly used attributes in the tests
+ # Each test takes these ones and then adds/modifies/deletes particular fields
+ return (
+ # test input ONNX attributes
+ dict(
+ border=[5, 10, 15, 20],
+ ),
+ # reference output Node attributes
+ dict(
+ op='Crop',
+ crop_begin=np.array([10, 5]),
+ crop_end=np.array([20, 15]),
+ axis=np.array([2, 3])
+ )
+ )
+
+ @staticmethod
+ def _extract(inp):
+ node = __class__._create_node(inp)
+ CropFrontExtractor.extract(node)
+ return node.graph.node[node.id]
+
+ def _match(self, out, ref):
+ for key in ref.keys():
+ status = out[key] == ref[key]
+ if type(status) in [list, np.ndarray]:
+ status = np.all(status)
+ self.assertTrue(status, 'Mismatch for field {}, observed: {}, expected: {}'.format(key, out[key], ref[key]))
+
+ def test_default(self):
+ inp, ref = self._base_attrs()
+ out = self._extract(inp)
+ self._match(out, ref)
+
+ def test_with_scale(self):
+ inp, ref = self._base_attrs()
+ inp['scale'] = np.array([34, 50])
+
+ del ref['crop_begin']
+ del ref['crop_end']
+ ref['dim'] = np.array([34, 50])
+ ref['offset'] = np.array([10, 5])
+
+ out = self._extract(inp)
+ self._match(out, ref)
diff --git a/model-optimizer/extensions/front/onnx/elu_ext_test.py b/model-optimizer/extensions/front/onnx/elu_ext_test.py
new file mode 100644
index 000000000..e509e4e78
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/elu_ext_test.py
@@ -0,0 +1,58 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import onnx
+from generator import generator, generate
+
+from extensions.front.onnx.elu_ext import EluFrontExtractor
+from mo.ops.activation import Activation
+from mo.ops.op import Op
+from mo.utils.unittest.extractors import PB
+
+
+@generator
+class TestEluONNXExt(unittest.TestCase):
+ @staticmethod
+ def _create_elu_node(alpha=1.0):
+ pb = onnx.helper.make_node(
+ 'Elu',
+ inputs=['x'],
+ outputs=['y'],
+ alpha=alpha
+ )
+ node = PB({'pb': pb})
+ return node
+
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Elu'] = Activation
+
+ @generate(*[1.0, 2.0, 3.0])
+ def test_elu_ext(self, alpha):
+ node = self._create_elu_node(alpha)
+ EluFrontExtractor.extract(node)
+
+ exp_res = {
+ 'type': 'Activation',
+ 'operation': 'elu',
+ 'alpha': alpha,
+ 'infer': Activation.infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/onnx/flatten_ext_test.py b/model-optimizer/extensions/front/onnx/flatten_ext_test.py
new file mode 100644
index 000000000..5498343a1
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/flatten_ext_test.py
@@ -0,0 +1,57 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import onnx
+from generator import generator, generate
+
+from extensions.front.onnx.flatten_ext import FlattenFrontExtractor
+from mo.ops.flatten_onnx import FlattenONNX
+from mo.ops.op import Op
+from mo.utils.unittest.extractors import PB
+
+
+@generator
+class TestFlattenONNXExt(unittest.TestCase):
+ @staticmethod
+ def _create_flatten_node(axis):
+ pb = onnx.helper.make_node(
+ 'Flatten',
+ inputs=['a'],
+ outputs=['b'],
+ axis=axis,
+ )
+ node = PB({'pb': pb})
+ return node
+
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Flatten'] = FlattenONNX
+
+ @generate(*[x for x in range(4)])
+ def test_flatten_ext(self, axis):
+ node = self._create_flatten_node(axis)
+ FlattenFrontExtractor.extract(node)
+
+ exp_res = {
+ 'type': 'Reshape',
+ 'axis': axis,
+ 'infer': FlattenONNX.infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/onnx/gather_ext_test.py b/model-optimizer/extensions/front/onnx/gather_ext_test.py
new file mode 100644
index 000000000..d91c7932c
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/gather_ext_test.py
@@ -0,0 +1,57 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import onnx
+from generator import generator, generate
+
+from extensions.front.onnx.gather_ext import GatherFrontExtractor
+from extensions.ops.gather import Gather
+from mo.ops.op import Op
+from mo.utils.unittest.extractors import PB
+
+
+@generator
+class TestGatherONNXExt(unittest.TestCase):
+ @staticmethod
+ def _create_gather_node(axis=0):
+ pb = onnx.helper.make_node(
+ 'Gather',
+ inputs=['data', 'indices'],
+ outputs=['y'],
+ axis=axis,
+ )
+ node = PB({'pb': pb})
+ return node
+
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Gather'] = Gather
+
+ @generate(*[0, 1, 2, 3])
+ def test_gather_ext(self, axis):
+ node = self._create_gather_node(axis)
+ GatherFrontExtractor.extract(node)
+
+ exp_res = {
+ 'type': 'Gather',
+ 'axis': axis,
+ 'infer': Gather.infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/onnx/image_scaler_ext_test.py b/model-optimizer/extensions/front/onnx/image_scaler_ext_test.py
new file mode 100644
index 000000000..8f5fb04dd
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/image_scaler_ext_test.py
@@ -0,0 +1,52 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+import onnx
+
+from extensions.front.onnx.image_scaler_ext import ImageScalerFrontExtractor
+from mo.utils.unittest.extractors import PB
+
+
+class TestImageScalerONNXExt(unittest.TestCase):
+ @staticmethod
+ def _create_image_scaler_node():
+ pb = onnx.helper.make_node(
+ 'ImageScaler',
+ inputs=['a'],
+ outputs=['b'],
+ scale=1.0,
+ bias=[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0],
+ )
+ node = PB({'pb': pb, 'graph': PB({'graph': {'layout': 'NCHW'}})})
+ return node
+
+ def test_image_scaler_ext(self):
+ node = self._create_image_scaler_node()
+ ImageScalerFrontExtractor.extract(node)
+
+ exp_res = {
+ 'scale': 1.0,
+ 'bias': [[[1.0]], [[2.0]], [[3.0]], [[4.0]], [[5.0]], [[6.0]], [[7.0]], [[8.0]]],
+ }
+
+ for key in exp_res.keys():
+ if type(node[key]) in [list, np.ndarray]:
+ self.assertTrue(np.array_equal(np.array(node[key]), np.array(exp_res[key])))
+ else:
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/onnx/instance_normalization_ext_test.py b/model-optimizer/extensions/front/onnx/instance_normalization_ext_test.py
new file mode 100644
index 000000000..c38a30f4a
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/instance_normalization_ext_test.py
@@ -0,0 +1,44 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import onnx
+
+from extensions.front.onnx.instance_normalization_ext import InstanceNormalizationExtractor
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class TestInstanceNormalization(BaseExtractorsTestingClass):
+ @staticmethod
+ def _create_node():
+ pb = onnx.helper.make_node(
+ 'InstanceNormalization',
+ inputs=['a'],
+ outputs=['b'],
+ epsilon=0.5,
+ )
+ node = PB({'pb': pb})
+ return node
+
+ def test_image_scaler_ext(self):
+ node = self._create_node()
+ InstanceNormalizationExtractor.extract(node)
+ self.res = node
+
+ self.expected = {
+ 'epsilon': 0.5,
+ }
+
+ self.compare()
diff --git a/model-optimizer/extensions/front/onnx/pad_ext_test.py b/model-optimizer/extensions/front/onnx/pad_ext_test.py
new file mode 100644
index 000000000..1f4f25d6c
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/pad_ext_test.py
@@ -0,0 +1,80 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import onnx
+
+from extensions.front.onnx.pad_ext import PadFrontExtractor
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class TestPad(BaseExtractorsTestingClass):
+ @staticmethod
+ def _create_node(pads=None, value=None, mode=None):
+ if pads is None:
+ pads = [1, 2, 3, 4]
+ if value is None:
+ value = 0.0
+ if mode is None:
+ mode = 'constant'
+ pb = onnx.helper.make_node(
+ 'Pad',
+ pads=pads,
+ mode=mode,
+ value=value,
+ inputs=['a'],
+ outputs=['b']
+ )
+ node = PB({'pb': pb})
+ return node
+
+ def test_ok(self):
+ node = self._create_node()
+ PadFrontExtractor.extract(node)
+ self.res = node
+
+ self.expected = {
+ 'pads': [[1, 3], [2, 4]],
+ 'mode': 'constant',
+ 'fill_value': 0
+ }
+
+ self.compare()
+
+ def test_reflect(self):
+ node = self._create_node(mode='reflect')
+ PadFrontExtractor.extract(node)
+ self.res = node
+
+ self.expected = {
+ 'pads': [[1, 3], [2, 4]],
+ 'mode': 'reflect',
+ 'fill_value': 0
+ }
+
+ self.compare()
+
+ def test_non_zero_fill_value(self):
+ node = self._create_node(value=1.0)
+ PadFrontExtractor.extract(node)
+ self.res = node
+
+ self.expected = {
+ 'pads': [[1, 3], [2, 4]],
+ 'mode': 'constant',
+ 'fill_value': 1.0
+ }
+
+ self.compare()
diff --git a/model-optimizer/extensions/front/onnx/sigmoid_ext_test.py b/model-optimizer/extensions/front/onnx/sigmoid_ext_test.py
new file mode 100644
index 000000000..3d25ea164
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/sigmoid_ext_test.py
@@ -0,0 +1,62 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+import onnx
+
+from extensions.front.onnx.sigmoid_ext import SigmoidFrontExtractor
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class SigmoidONNXExtractorTest(unittest.TestCase):
+ @staticmethod
+ def _create_node():
+ pb = onnx.helper.make_node("Sigmoid", ["X"], ["Y"])
+ graph = build_graph({'node_0': {'pb': pb}}, [])
+ return Node(graph, 'node_0')
+
+ @staticmethod
+ def _base_attrs():
+ # Commonly used attributes in the tests
+ # Each test takes these ones and then adds/modifies/deletes particular fields
+ return (
+ # reference output Node attributes
+ dict(
+ op='Activation',
+ operation='sigmoid'
+ )
+ )
+
+ @staticmethod
+ def _extract():
+ node = __class__._create_node()
+ SigmoidFrontExtractor.extract(node)
+ return node.graph.node[node.id]
+
+ def _match(self, out, ref):
+ for key in ref.keys():
+ status = out[key] == ref[key]
+ if type(status) in [list, np.ndarray]:
+ status = np.all(status)
+ self.assertTrue(status, 'Mismatch for field {}, observed: {}, expected: {}'.format(key, out[key], ref[key]))
+
+ def test_default(self):
+ ref = self._base_attrs()
+ out = self._extract()
+ self._match(out, ref)
diff --git a/model-optimizer/extensions/front/onnx/slice_ext_test.py b/model-optimizer/extensions/front/onnx/slice_ext_test.py
new file mode 100644
index 000000000..74ab96a83
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/slice_ext_test.py
@@ -0,0 +1,75 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+import onnx
+from generator import generator, generate
+
+from extensions.front.onnx.slice_ext import SliceFrontExtractor
+from mo.ops.op import Op
+from mo.ops.slice import Slice
+from mo.utils.unittest.extractors import PB
+
+
+@generator
+class TestSliceONNXExt(unittest.TestCase):
+ @staticmethod
+ def _create_slice_node(axes, starts, ends):
+ if axes is None:
+ pb = onnx.helper.make_node(
+ 'Slice',
+ inputs=['x'],
+ outputs=['y'],
+ starts=starts,
+ ends=ends,
+ )
+ else:
+ pb = onnx.helper.make_node(
+ 'Slice',
+ inputs=['x'],
+ outputs=['y'],
+ axes=axes,
+ starts=starts,
+ ends=ends,
+ )
+
+ node = PB({'pb': pb})
+ return node
+
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Slice'] = Slice
+
+ @generate(*[([0, 1], [0, 0], [28, 28]), (None, [0, 0], [28, 28])])
+ def test_slice_ext(self, axes, starts, ends):
+ node = self._create_slice_node(axes, starts, ends)
+ SliceFrontExtractor.extract(node)
+
+ exp_res = {
+ 'op': 'Slice',
+ 'axis': axes,
+ 'start': starts,
+ 'end': ends,
+ 'infer': Slice.infer
+ }
+
+ for key in exp_res.keys():
+ if type(node[key]) in [list, np.ndarray]:
+ self.assertTrue(np.array_equal(np.array(node[key]), np.array(exp_res[key])))
+ else:
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/onnx/squeeze_ext_test.py b/model-optimizer/extensions/front/onnx/squeeze_ext_test.py
new file mode 100644
index 000000000..5c697289e
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/squeeze_ext_test.py
@@ -0,0 +1,68 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+import onnx
+from generator import generator, generate
+
+from extensions.front.onnx.squeeze_ext import SqueezeFrontExtractor
+from mo.ops.op import Op
+from mo.ops.squeeze import Squeeze
+from mo.utils.unittest.extractors import PB
+
+
+@generator
+class TestSqueezeONNXExt(unittest.TestCase):
+ @staticmethod
+ def _create_squeeze_node(axes):
+ if axes is None:
+ pb = onnx.helper.make_node(
+ 'Squeeze',
+ inputs=['x'],
+ outputs=['y'],
+ )
+ else:
+ pb = onnx.helper.make_node(
+ 'Squeeze',
+ inputs=['x'],
+ outputs=['y'],
+ axes=axes,
+ )
+
+ node = PB({'pb': pb})
+ return node
+
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Squeeze'] = Squeeze
+
+ @generate(*[[0, 1, 2, 3], [1], None])
+ def test_squeeze_ext(self, axes):
+ node = self._create_squeeze_node(axes)
+ SqueezeFrontExtractor.extract(node)
+
+ exp_res = {
+ 'type': 'Reshape',
+ 'squeeze_dims': axes,
+ }
+
+ for key in exp_res.keys():
+ if type(node[key]) in [list, np.ndarray]:
+ self.assertTrue(np.array_equal(np.array(node[key]), np.array(exp_res[key])))
+ else:
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/onnx/tanh_ext_test.py b/model-optimizer/extensions/front/onnx/tanh_ext_test.py
new file mode 100644
index 000000000..25b858628
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/tanh_ext_test.py
@@ -0,0 +1,62 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+import onnx
+
+from extensions.front.onnx.tanh_ext import TanhFrontExtractor
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+
+
+class TanhONNXExtractorTest(unittest.TestCase):
+ @staticmethod
+ def _create_node():
+ pb = onnx.helper.make_node("Tanh", ["X"], ["Y"])
+ graph = build_graph({'node_0': {'pb': pb}}, [])
+ return Node(graph, 'node_0')
+
+ @staticmethod
+ def _base_attrs():
+ # Commonly used attributes in the tests
+ # Each test takes these ones and then adds/modifies/deletes particular fields
+ return (
+ # reference output Node attributes
+ dict(
+ op='Activation',
+ operation='tanh'
+ )
+ )
+
+ @staticmethod
+ def _extract():
+ node = __class__._create_node()
+ TanhFrontExtractor.extract(node)
+ return node.graph.node[node.id]
+
+ def _match(self, out, ref):
+ for key in ref.keys():
+ status = out[key] == ref[key]
+ if type(status) in [list, np.ndarray]:
+ status = np.all(status)
+ self.assertTrue(status, 'Mismatch for field {}, observed: {}, expected: {}'.format(key, out[key], ref[key]))
+
+ def test_default(self):
+ ref = self._base_attrs()
+ out = self._extract()
+ self._match(out, ref)
diff --git a/model-optimizer/extensions/front/onnx/transpose_ext_test.py b/model-optimizer/extensions/front/onnx/transpose_ext_test.py
new file mode 100644
index 000000000..2880c2d75
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/transpose_ext_test.py
@@ -0,0 +1,75 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import itertools
+import unittest
+
+import numpy as np
+import onnx
+from generator import generator, generate
+
+from extensions.front.onnx.transpose_ext import TransposeFrontExtractor
+from mo.ops.op import Op
+from mo.ops.permute import Permute
+from mo.utils.unittest.extractors import PB
+
+
+@generator
+class TestTransposeONNXExt(unittest.TestCase):
+ @staticmethod
+ def _create_transpose_node(order: list):
+ if order is None:
+ # Default transpose
+ pb = onnx.helper.make_node(
+ 'Transpose',
+ inputs=['data'],
+ outputs=['transposed'],
+ )
+ else:
+ # Transpose with order
+ pb = onnx.helper.make_node(
+ 'Transpose',
+ inputs=['data'],
+ outputs=['transposed'],
+ perm=order
+ )
+ node = PB({'pb': pb})
+ return node
+
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Permute'] = Permute
+ pass
+
+ # This generator generates all permutations for [0,1,2,3] and [0,1,2] orders
+ @generate(*[list(order) for order in list(itertools.permutations(np.arange(4)))] +
+ [list(order) for order in list(itertools.permutations(np.arange(3)))] + [None])
+ def test_transpose_ext(self, order):
+ node = self._create_transpose_node(order)
+ TransposeFrontExtractor.extract(node)
+
+ exp_res = {
+ 'type': 'Permute',
+ 'order': order,
+ 'infer': Permute.infer
+ }
+
+ for key in exp_res.keys():
+ if isinstance(exp_res[key], list):
+ self.assertTrue(np.array_equal(node[key], exp_res[key]),
+ "Orders are not the same: {} and {}".format(node[key], exp_res[key]))
+ else:
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/onnx/unsqueeze_ext_test.py b/model-optimizer/extensions/front/onnx/unsqueeze_ext_test.py
new file mode 100644
index 000000000..7cdcdaeea
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/unsqueeze_ext_test.py
@@ -0,0 +1,60 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+import onnx
+from generator import generator, generate
+
+from extensions.front.onnx.unsqueeze_ext import UnsqueezeFrontExtractor
+from mo.ops.op import Op
+from mo.ops.unsqueeze import Unsqueeze
+from mo.utils.unittest.extractors import PB
+
+
+@generator
+class TestUnsqueezeONNXExt(unittest.TestCase):
+ @staticmethod
+ def _create_unsqueeze_node(axes):
+ pb = onnx.helper.make_node(
+ 'Unsqueeze',
+ inputs=['x'],
+ outputs=['y'],
+ axes=axes,
+ )
+
+ node = PB({'pb': pb})
+ return node
+
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Unsqueeze'] = Unsqueeze
+
+ @generate(*[[0, 1, 2, 3], [1]])
+ def test_unsqueeze_ext(self, axes):
+ node = self._create_unsqueeze_node(axes)
+ UnsqueezeFrontExtractor.extract(node)
+
+ exp_res = {
+ 'unsqueeze_dims': axes,
+ }
+
+ for key in exp_res.keys():
+ if type(node[key]) in [list, np.ndarray]:
+ self.assertTrue(np.array_equal(np.array(node[key]), np.array(exp_res[key])))
+ else:
+ self.assertEqual(node[key], exp_res[key])
diff --git a/model-optimizer/extensions/front/onnx/upsample_ext_test.py b/model-optimizer/extensions/front/onnx/upsample_ext_test.py
new file mode 100644
index 000000000..e3634174e
--- /dev/null
+++ b/model-optimizer/extensions/front/onnx/upsample_ext_test.py
@@ -0,0 +1,104 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import onnx
+
+from extensions.front.onnx.upsample_ext import UpsampleFrontExtractor
+from mo.utils.unittest.graph import build_graph
+from mo.graph.graph import Node
+from mo.utils.error import Error
+from mo.utils.unittest.extractors import BaseExtractorsTestingClass
+
+
+class UpsampleONNXExtractorTest(BaseExtractorsTestingClass):
+ @staticmethod
+ def _create_node(attrs: dict):
+ pb = onnx.helper.make_node("Upsample", ["X"], ["Y"], **attrs)
+ graph = build_graph({'node_0': {'pb': pb}}, [])
+ return Node(graph, 'node_0')
+
+ @staticmethod
+ def _base_attrs():
+ # Commonly used attributes in the tests
+ # Each test takes these ones and then adds/modifies/deletes particular fields
+ return (
+ # test input ONNX attributes
+ dict(
+ mode='nearest',
+ width_scale=2.0,
+ height_scale=2.0,
+ ),
+ # reference output Node attributes
+ dict(
+ type='Resample',
+ resample_type='caffe.ResampleParameter.NEAREST',
+ factor=2,
+ antialias=0,
+ )
+ )
+
+ @staticmethod
+ def _extract(inp):
+ node = __class__._create_node(inp)
+ UpsampleFrontExtractor.extract(node)
+ return node
+
+ def _match(self, out, ref):
+ self.res = out
+ self.expected = ref
+ self.compare()
+
+ def test_all_valid_default(self):
+ inp, ref = self._base_attrs()
+ out = self._extract(inp)
+ self._match(out, ref)
+
+ def test_invalid_mode(self):
+ inp, ref = self._base_attrs()
+ inp['mode'] = 'invalid_mode'
+ with self.assertRaisesRegex(Error, '.*decoding Upsample.*supported modes.*'):
+ out = self._extract(inp)
+
+ def test_unsupported_linear(self):
+ inp, ref = self._base_attrs()
+ inp['mode'] = 'linear'
+ with self.assertRaisesRegex(Error, '.*Only nearest is supported.*'):
+ out = self._extract(inp)
+
+ def test_unsupported_scale(self):
+ inp, ref = self._base_attrs()
+ inp['scales'] = [2.0, 2.0]
+ with self.assertRaisesRegex(Error, '.*Only scale_width and scale_height are supported.*'):
+ out = self._extract(inp)
+
+ def test_missing_width_scale(self):
+ inp, ref = self._base_attrs()
+ del inp['width_scale']
+ with self.assertRaisesRegex(Error, '.*One/both of widths_scale.*and height_scale.*is not defined.*'):
+ out = self._extract(inp)
+
+ def test_missing_height_scale(self):
+ inp, ref = self._base_attrs()
+ del inp['height_scale']
+ with self.assertRaisesRegex(Error, '.*One/both of widths_scale.*and height_scale.*is not defined.*'):
+ out = self._extract(inp)
+
+ def test_different_scales(self):
+ inp, ref = self._base_attrs()
+ inp['height_scale'] = 2.0
+ inp['width_scale'] = 3.0
+ with self.assertRaisesRegex(Error, '.*different widths_scale.*and height_scale.*not supported.*'):
+ out = self._extract(inp)
diff --git a/model-optimizer/extensions/front/reciprocal_test.py b/model-optimizer/extensions/front/reciprocal_test.py
new file mode 100644
index 000000000..527cb7e3f
--- /dev/null
+++ b/model-optimizer/extensions/front/reciprocal_test.py
@@ -0,0 +1,55 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.reciprocal import ReciprocalReplacer
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+
+class ReciprocalReplacerTests(unittest.TestCase):
+ @staticmethod
+ def _create_graphs():
+ return (
+ build_graph(
+ {'placeholder': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'reciprocal': {'kind': 'op', 'op': 'Reciprocal'}},
+ [('placeholder', 'reciprocal')]),
+
+ build_graph(
+ {'placeholder': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'power': {'type': 'Power', 'kind': 'op', 'op': 'Power', 'scale': 1, 'power': -1, 'shift': 0}},
+ [('placeholder', 'power')])
+ )
+
+ def test_replace_reciprocal(self):
+ graph, graph_ref = __class__._create_graphs()
+
+ pattern = ReciprocalReplacer()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'reciprocal/power_', last_node_ref='power', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_neg_replace_reciprocal(self):
+ graph, graph_ref = __class__._create_graphs()
+ graph_ref.node['power']['power'] = 0
+
+ pattern = ReciprocalReplacer()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'reciprocal/power_', last_node_ref='power', check_op_attrs=True)
+ self.assertTrue(not flag) \ No newline at end of file
diff --git a/model-optimizer/extensions/front/tf/ObjectDetectionAPI_test.py b/model-optimizer/extensions/front/tf/ObjectDetectionAPI_test.py
new file mode 100644
index 000000000..d9056efe4
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/ObjectDetectionAPI_test.py
@@ -0,0 +1,127 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import networkx as nx
+
+from extensions.front.tf.ObjectDetectionAPI import calculate_shape_keeping_aspect_ratio, \
+ calculate_placeholder_spatial_shape
+from mo.front.subgraph_matcher import SubgraphMatch
+from mo.utils.custom_replacement_config import CustomReplacementDescriptor
+from mo.utils.error import Error
+
+
+class FakePipelineConfig:
+ def __init__(self, model_params: dict):
+ self._model_params = model_params
+
+ def get_param(self, param: str):
+ if param not in self._model_params:
+ return None
+ return self._model_params[param]
+
+
+class TestCalculateShape(unittest.TestCase):
+ min_size = 600
+ max_size = 1024
+
+ def test_calculate_shape_1(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(100, 300, self.min_size, self.max_size), (341, 1024))
+
+ def test_calculate_shape_2(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(100, 600, self.min_size, self.max_size), (171, 1024))
+
+ def test_calculate_shape_3(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(100, 3000, self.min_size, self.max_size), (34, 1024))
+
+ def test_calculate_shape_4(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(300, 300, self.min_size, self.max_size), (600, 600))
+
+ def test_calculate_shape_5(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(300, 400, self.min_size, self.max_size), (600, 800))
+
+ def test_calculate_shape_6(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(300, 600, self.min_size, self.max_size), (512, 1024))
+
+ def test_calculate_shape_7(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(1000, 2500, self.min_size, self.max_size),
+ (410, 1024))
+
+ def test_calculate_shape_8(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(1800, 2000, self.min_size, self.max_size),
+ (600, 667))
+
+ def test_calculate_shape_11(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(300, 100, self.min_size, self.max_size), (1024, 341))
+
+ def test_calculate_shape_12(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(600, 100, self.min_size, self.max_size), (1024, 171))
+
+ def test_calculate_shape_13(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(3000, 100, self.min_size, self.max_size), (1024, 34))
+
+ def test_calculate_shape_15(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(400, 300, self.min_size, self.max_size), (800, 600))
+
+ def test_calculate_shape_16(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(600, 300, self.min_size, self.max_size), (1024, 512))
+
+ def test_calculate_shape_17(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(2500, 1000, self.min_size, self.max_size),
+ (1024, 410))
+
+ def test_calculate_shape_18(self):
+ self.assertTupleEqual(calculate_shape_keeping_aspect_ratio(2000, 1800, self.min_size, self.max_size),
+ (667, 600))
+
+
+class TestCalculatePlaceholderSpatialShape(unittest.TestCase):
+ def setUp(self):
+ self.graph = nx.MultiDiGraph()
+ self.graph.graph['user_shapes'] = None
+ self.replacement_desc = CustomReplacementDescriptor('dummy_id', {})
+ self.match = SubgraphMatch(self.graph, self.replacement_desc, [], [], [], '')
+ self.pipeline_config = FakePipelineConfig({})
+
+ def test_default_fixed_shape_resizer(self):
+ self.pipeline_config._model_params['resizer_image_height'] = 300
+ self.pipeline_config._model_params['resizer_image_width'] = 600
+ self.assertTupleEqual((300, 600),
+ calculate_placeholder_spatial_shape(self.graph, self.match, self.pipeline_config))
+
+ def test_fixed_shape_resizer_overrided_by_user(self):
+ self.pipeline_config._model_params['resizer_image_height'] = 300
+ self.pipeline_config._model_params['resizer_image_width'] = 600
+ self.graph.graph['user_shapes'] = {'image_tensor': [{'shape': [1, 400, 500, 3]}]}
+ self.assertTupleEqual((400, 500),
+ calculate_placeholder_spatial_shape(self.graph, self.match, self.pipeline_config))
+
+ def test_default_keep_aspect_ratio_resizer(self):
+ self.pipeline_config._model_params['resizer_min_dimension'] = 600
+ self.pipeline_config._model_params['resizer_max_dimension'] = 1024
+ self.assertTupleEqual((600, 600),
+ calculate_placeholder_spatial_shape(self.graph, self.match, self.pipeline_config))
+
+ def test_keep_aspect_ratio_resizer_overrided_by_user(self):
+ self.pipeline_config._model_params['resizer_min_dimension'] = 600
+ self.pipeline_config._model_params['resizer_max_dimension'] = 1024
+ self.graph.graph['user_shapes'] = {'image_tensor': [{'shape': [1, 400, 300, 3]}]}
+ self.assertTupleEqual((800, 600),
+ calculate_placeholder_spatial_shape(self.graph, self.match, self.pipeline_config))
+
+ def test_missing_input_shape_information(self):
+ self.assertRaises(Error, calculate_placeholder_spatial_shape, self.graph, self.match, self.pipeline_config)
diff --git a/model-optimizer/extensions/front/tf/concat_ext_test.py b/model-optimizer/extensions/front/tf/concat_ext_test.py
new file mode 100644
index 000000000..9cf90216d
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/concat_ext_test.py
@@ -0,0 +1,34 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from extensions.front.tf.concat_ext import ConcatFrontExtractor
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class ConcatExtractorTest(BaseExtractorsTestingClass):
+ def test_concat(self):
+ node = PB({'pb': PB({'attr': {'N': PB({'i': 4})}})})
+ self.expected = {
+ 'N': 4,
+ 'simple_concat': True,
+ 'type': 'Concat',
+ 'op': 'Concat',
+ 'kind': 'op',
+ 'axis': 1
+ }
+ ConcatFrontExtractor.extract(node)
+ self.res = node
+ self.compare()
diff --git a/model-optimizer/extensions/front/tf/concat_test.py b/model-optimizer/extensions/front/tf/concat_test.py
new file mode 100644
index 000000000..abee3b0b0
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/concat_test.py
@@ -0,0 +1,47 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.tf.concat import Concat
+from mo.utils.unittest.graph import build_graph_with_edge_attrs
+
+
+class TestConcatEdgesReshuffler(unittest.TestCase):
+ def test_concat_edges_reshaffle(self):
+ graph = build_graph_with_edge_attrs(
+ {'axis': {},
+ 'input_1': {},
+ 'input_2': {},
+ 'input_3': {},
+ 'concat': {'op': 'Concat', 'simple_concat': True, 'axis': 1},
+ },
+ [('axis', 'concat', {'in': 0}),
+ ('input_1', 'concat', {'in': 1}),
+ ('input_2', 'concat', {'in': 2}),
+ ('input_3', 'concat', {'in': 3})],
+ )
+ Concat().find_and_replace_pattern(graph=graph)
+ for u, v, attrs in graph.in_edges('concat', data=True):
+ if attrs['in'] == 0:
+ self.assertEqual(u, 'input_1')
+ if attrs['in'] == 1:
+ self.assertEqual(u, 'input_2')
+ if attrs['in'] == 2:
+ self.assertEqual(u, 'input_3')
+ if attrs['in'] == 3:
+ self.assertEqual(u, 'axis')
+ self.assertTrue('axis' not in graph.node['concat'])
diff --git a/model-optimizer/extensions/front/tf/conv_ext_test.py b/model-optimizer/extensions/front/tf/conv_ext_test.py
new file mode 100644
index 000000000..d420f9101
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/conv_ext_test.py
@@ -0,0 +1,140 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from extensions.front.tf.conv_ext import Conv2DFrontExtractor, DepthwiseConv2dNativeFrontExtractor
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class ConvExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.strides = [1, 2, 3, 4]
+ cls.dilations = [1, 1, 1, 1]
+
+ def test_conv_2d_defaults(self):
+ node = PB({'pb': PB({'attr': {
+ 'data_format': PB({
+ 's': b"NHWC"
+ }),
+ 'strides': PB({
+ 'list': PB({"i": self.strides})
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ }),
+ 'dilations': PB({
+ 'list': PB({"i": [1, 1, 1, 1]})
+ })
+ }})})
+ self.expected = {
+ 'bias_addable': True,
+ 'dilation': np.array([1, 1, 1, 1], dtype=np.int8),
+ 'type': 'Convolution',
+ 'layout': 'NHWC',
+ }
+ Conv2DFrontExtractor.extract(node)
+ self.res = node
+ self.expected_call_args = (None, False)
+ self.compare()
+
+ def test_conv2d_nhwc(self):
+ node = PB({'pb': PB({'attr': {
+ 'data_format': PB({
+ 's': b"NHWC"
+ }),
+ 'strides': PB({
+ 'list': PB({"i": self.strides})
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ }),
+ 'dilations': PB({
+ 'list': PB({"i": [1, 1, 1, 1]})
+ })
+ }})})
+ self.expected = {
+ # spatial_dims = [1, 2] will be detected in infer function
+ "channel_dims": [3],
+ "batch_dims": [0],
+ "input_feature_channel": 2,
+ "output_feature_channel": 3,
+ 'dilation': np.array([1, 1, 1, 1], dtype=np.int8),
+ 'stride': np.array(self.strides, dtype=np.int8),
+ }
+ Conv2DFrontExtractor.extract(node)
+ self.res = node
+ self.expected_call_args = (None, False)
+ self.compare()
+
+ def test_conv2d_nchw(self):
+ node = PB({'pb': PB({'attr': {
+ 'data_format': PB({
+ 's': b"NCHW"
+ }),
+ 'strides': PB({
+ 'list': PB({"i": self.strides})
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ }),
+ 'dilations': PB({
+ 'list': PB({"i": [1, 1, 1, 1]})
+ })
+ }})})
+ self.expected = {
+ # spatial_dims = [2, 3] will be detected in infer function
+ "channel_dims": [1],
+ "batch_dims": [0],
+ "input_feature_channel": 2,
+ "output_feature_channel": 3,
+ 'dilation': np.array([1, 1, 1, 1], dtype=np.int8),
+ 'stride': np.array(self.strides, dtype=np.int8),
+ }
+ Conv2DFrontExtractor.extract(node)
+ self.res = node
+ self.expected_call_args = (None, False)
+ self.compare()
+
+ def test_conv2d_depthwise(self):
+ node = PB({'pb': PB({'attr': {
+ 'data_format': PB({
+ 's': b"NHWC"
+ }),
+ 'strides': PB({
+ 'list': PB({"i": self.strides}),
+ }),
+ 'dilations': PB({
+ 'list': PB({"i": self.dilations}),
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ })
+ }})})
+ self.expected = {
+ # spatial_dims = [1, 2] will be detected in infer function
+ "channel_dims": [3],
+ "batch_dims": [0],
+ "input_feature_channel": 2,
+ "output_feature_channel": 2,
+ 'dilation': np.array([1, 1, 1, 1], dtype=np.int8),
+ 'stride': np.array(self.strides, dtype=np.int8),
+ }
+ DepthwiseConv2dNativeFrontExtractor.extract(node)
+ self.res = node
+ self.expected_call_args = (None, True)
+ self.compare()
diff --git a/model-optimizer/extensions/front/tf/deconv_ext_test.py b/model-optimizer/extensions/front/tf/deconv_ext_test.py
new file mode 100644
index 000000000..c11d4da45
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/deconv_ext_test.py
@@ -0,0 +1,100 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from extensions.front.tf.deconv_ext import Conv2DBackpropInputFrontExtractor
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class DeconvolutionExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.strides = [1, 2, 3, 4]
+
+ def test_deconv2d_defaults(self):
+ node = PB({'pb': PB({'attr': {
+ 'data_format': PB({
+ 's': b"NHWC"
+ }),
+ 'strides': PB({
+ 'list': PB({"i": self.strides})
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ })
+ }})})
+ self.expected = {
+ 'bias_addable': True,
+ 'pad': None, # will be inferred when input shape is known
+ 'pad_spatial_shape': None,
+ 'output_spatial_shape': None,
+ 'output_shape': None,
+ 'group': None,
+ }
+ Conv2DBackpropInputFrontExtractor.extract(node)
+ self.res = node
+ self.expected_call_args = (None, False)
+ self.compare()
+
+ def test_deconv2d_nhwc(self):
+ node = PB({'pb': PB({'attr': {
+ 'data_format': PB({
+ 's': b"NHWC"
+ }),
+ 'strides': PB({
+ 'list': PB({"i": self.strides})
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ })
+ }})})
+
+ self.expected = {
+ "spatial_dims": [1, 2],
+ "channel_dims": [3],
+ "batch_dims": [0],
+ 'stride': np.array(self.strides, dtype=np.int8),
+ }
+
+ Conv2DBackpropInputFrontExtractor.extract(node)
+ self.res = node
+ self.expected_call_args = (None, False)
+ self.compare()
+
+ def test_deconv2d_nchw(self):
+ node = PB({'pb': PB({'attr': {
+ 'data_format': PB({
+ 's': b"NCHW"
+ }),
+ 'strides': PB({
+ 'list': PB({"i": self.strides})
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ })
+ }})})
+ self.expected = {
+ "spatial_dims": [2, 3],
+ "channel_dims": [1],
+ "batch_dims": [0],
+ 'stride': np.array(self.strides, dtype=np.int8),
+ }
+
+ Conv2DBackpropInputFrontExtractor.extract(node)
+ self.res = node
+ self.expected_call_args = (None, False)
+ self.compare()
diff --git a/model-optimizer/extensions/front/tf/fifo_replacer_test.py b/model-optimizer/extensions/front/tf/fifo_replacer_test.py
new file mode 100644
index 000000000..e1150c2e6
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/fifo_replacer_test.py
@@ -0,0 +1,79 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.front.tf.fifo_replacer import FIFOQueue
+from mo.utils.unittest.graph import build_graph_with_edge_attrs
+
+
+class TestFIFOQueueReplacement(unittest.TestCase):
+ def test_fifo_with_label_batch(self):
+ nodes = {
+ 'placeholder': {'op': 'Placeholder', 'data_type': np.int32, 'kind': 'op', 'shape': np.array(1)},
+ 'batch_join/fifo_queue': {'op': 'FIFOQueueV2', 'name': 'batch_join/fifo_queue',
+ 'shapes': np.array([[1, 2, 3]]), 'kind': 'op'},
+ 'batch_join': {'op': 'QueueDequeueUpToV2', 'kind': 'op'},
+ 'image_batch': {'op': 'Identity', 'data_type': np.float32, 'kind': 'op'},
+ 'label_batch': {'op': 'Identity', 'kind': 'op'},
+ 'label_batch_op_output': {'op': 'OpOutput', 'kind': 'op'},
+ }
+ edges = [
+ ('placeholder', 'batch_join', {'out': 0, 'in': 0}),
+ ('batch_join/fifo_queue', 'batch_join', {'out': 0, 'in': 1}),
+ ('batch_join', 'image_batch', {'out': 0, 'in': 0}),
+ ('batch_join', 'label_batch', {'out': 1, 'in': 0}),
+ ('label_batch', 'label_batch_op_output', {'out': 0, 'in': 0})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ tested_class = FIFOQueue()
+ tested_class.find_and_replace_pattern(graph=graph)
+ after_pattern = graph.nodes()
+ self.assertEqual(2, len(after_pattern))
+ try:
+ new_ph_dict = graph.node[[u for u, v in graph.in_edges('image_batch')][0]]
+ except Exception as e:
+ self.fail("Can't get new placeholder. Broken edge. Additional information: {}".format(e))
+ self.assertEqual(new_ph_dict['name'], 'batch_join/fifo_queue')
+ self.assertTrue(np.array_equal(new_ph_dict['shape'], [1, 2, 3]))
+
+ def test_fifo_with_out_label_batch(self):
+ nodes_no_label = {
+ 'placeholder': {'op': 'Placeholder', 'data_type': np.int32, 'kind': 'op', 'shape': np.array(0)},
+ 'batch_join/fifo_queue': {'op': 'FIFOQueueV2', 'name': 'batch_join/fifo_queue',
+ 'shapes': np.array([[1, 2, 3]]), 'kind': 'op'},
+ 'batch_join': {'op': 'QueueDequeueUpToV2', 'kind': 'op'},
+ 'image_batch': {'op': 'Identity', 'data_type': np.float32, 'kind': 'op'},
+ }
+ edges_no_label = [
+ ('placeholder', 'batch_join', {'out': 0}),
+ ('batch_join/fifo_queue', 'batch_join', {'out': 0}),
+ ('batch_join', 'image_batch', {'out': 0})
+ ]
+
+ graph = build_graph_with_edge_attrs(nodes_no_label, edges_no_label)
+ tested_class = FIFOQueue()
+ tested_class.find_and_replace_pattern(graph=graph)
+ after_pattern = graph.nodes()
+ self.assertEqual(2, len(after_pattern))
+ try:
+ new_ph_dict = graph.node[[u for u, v in graph.in_edges('image_batch')][0]]
+ except Exception as e:
+ self.fail("Can't get new placeholder. Broken edge. Additional information: {}".format(e))
+ self.assertEqual(new_ph_dict['name'], 'batch_join/fifo_queue')
+ self.assertTrue(np.array_equal(new_ph_dict['shape'], np.array([1, 2, 3])))
diff --git a/model-optimizer/extensions/front/tf/mvn_unrolled_test.py b/model-optimizer/extensions/front/tf/mvn_unrolled_test.py
new file mode 100644
index 000000000..de9618b1f
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/mvn_unrolled_test.py
@@ -0,0 +1,69 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+from extensions.front.tf.mvn_unrolled import MVNUnrolled
+from mo.ops.op import Op
+from mo.utils.unittest.graph import compare_graphs, build_graph_with_attrs
+from extensions.ops.mvn import MVN
+
+
+class MVNUnrolledMatchingTests(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['MVN'] = MVN
+
+ def test(self):
+ pattern_matcher = MVNUnrolled()
+ pattern = pattern_matcher.pattern()
+ graph = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'], update_edge_attrs=None,
+ new_nodes_with_attrs=[('reduction_indicies', {'kind': 'data'}),
+ ('conv2d', {'kind': 'op'}),
+ ('variance_reduction', {'kind': 'data'}),
+ ('pow2', {'kind': 'data'}),
+ ('eps', {'kind': 'data'}),
+ ('next_op', {'kind': 'op'})],
+ new_edges_with_attrs=[('reduction_indicies', 'mean', {'in': 1}),
+ ('conv2d', 'mean',{'in': 0, 'out': 1}),
+ ('variance_reduction', 'variance', {'in': 1}),
+ ('pow2', 'pow', {'in': 1}),
+ ('eps', 'add'), ('truediv', 'next_op')])
+ graph.graph['layout'] = 'NHWC'
+ pattern_matcher.find_and_replace_pattern(graph)
+
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'][:-1],
+ edges_with_attrs=pattern['edges'][:-2], update_edge_attrs=None,
+ new_nodes_with_attrs=[('reduction_indicies', {'kind':'data'}),
+ ('conv2d', {'kind':'op'}),
+ ('variance_reduction', {'kind':'data'}),
+ ('pow2', {'kind': 'data'}),
+ ('eps', {'kind': 'data'}),
+ ('mvn', {'kind': 'op', 'op': 'MVN'}),
+ ('next_op', {'kind': 'op'})],
+ new_edges_with_attrs=[('reduction_indicies', 'mean', {'in':1}),
+ ('conv2d', 'mean', {'in': 0}),
+ ('variance_reduction', 'variance',{'in': 1}),
+ ('pow2', 'pow', {'in': 1}),
+ ('eps', 'add'),
+ ('conv2d', 'mvn',{'in': 0}),
+ ('reduction_indicies', 'mvn', {'in': 1}),
+ ('variance_reduction', 'mvn',{'in': 2}),
+ ('pow2', 'mvn', {'in': 3}),
+ ('eps', 'mvn',{'in': 4}),
+ ('mvn', 'next_op')])
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'next_op', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/front/tf/next_iteration_ext_test.py b/model-optimizer/extensions/front/tf/next_iteration_ext_test.py
new file mode 100644
index 000000000..98e0ab6c7
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/next_iteration_ext_test.py
@@ -0,0 +1,30 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from extensions.front.tf.next_iteration_ext import NextIterationExtractor
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class TestNextIteration(BaseExtractorsTestingClass):
+ def test_is_cyclic(self):
+ pb = PB({})
+ node = PB({'pb': pb})
+ NextIterationExtractor.extract(node)
+ self.expected = {
+ 'is_cyclic': True,
+ }
+ self.res = node
+ self.compare()
diff --git a/model-optimizer/extensions/front/tf/pad_ext_test.py b/model-optimizer/extensions/front/tf/pad_ext_test.py
new file mode 100644
index 000000000..138b4f0e5
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/pad_ext_test.py
@@ -0,0 +1,27 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.front.tf.pad_ext import PadFrontExtractor
+from mo.utils.unittest.extractors import PB
+
+
+class TestPad(unittest.TestCase):
+ def test_no_pads(self):
+ node = PB({})
+ PadFrontExtractor.extract(node)
+ self.assertTrue(not 'pads' in node or node['pads'] is None)
diff --git a/model-optimizer/extensions/front/tf/pooling_ext_test.py b/model-optimizer/extensions/front/tf/pooling_ext_test.py
new file mode 100644
index 000000000..a03095ee9
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/pooling_ext_test.py
@@ -0,0 +1,154 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from extensions.front.tf.pooling_ext import AvgPoolFrontExtractor, MaxPoolFrontExtractor
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class PoolingExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.strides = [1, 2, 3, 4]
+ cls.ksize = [1, 3, 3, 1]
+ cls.patcher = 'mo.ops.pooling.Pooling.infer'
+
+ def test_pool_defaults(self):
+ pb = PB({'attr': {
+ 'data_format': PB({
+ 's': b"NHWC"
+ }),
+ 'strides': PB({
+ 'list': PB({
+ "i": self.strides
+ })
+ }),
+ 'ksize': PB({
+ 'list': PB({"i": self.ksize})
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ })
+ }})
+ self.expected = {
+ 'pad': None, # will be inferred when input shape is known
+ 'pad_spatial_shape': None,
+ 'type': 'Pooling',
+ 'exclude_pad': 'true',
+ }
+ node = PB({'pb': pb})
+ AvgPoolFrontExtractor.extract(node)
+ self.res = node
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = (None, None)
+ self.compare()
+
+ def test_avg_pool_nhwc(self):
+ pb = PB({'attr': {
+ 'data_format': PB({
+ 's': b"NHWC"
+ }),
+ 'strides': PB({
+ 'list': PB({"i": self.strides})
+ }),
+ 'ksize': PB({
+ 'list': PB({"i": self.ksize})
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ })
+ }})
+ self.expected = {
+ 'window': np.array(self.ksize, dtype=np.int8),
+ 'spatial_dims': [1, 2],
+ 'stride': np.array(self.strides, dtype=np.int8),
+ 'pool_method': "avg",
+ }
+ node = PB({'pb': pb})
+ AvgPoolFrontExtractor.extract(node)
+ self.res = node
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = (None, "avg")
+ self.compare()
+
+ def test_avg_pool_nchw(self):
+ pb = PB({'attr': {
+ 'data_format': PB({
+ 's': b"NCHW"
+ }),
+ 'strides': PB({
+ 'list': PB({
+ "i": self.strides
+ })
+ }),
+ 'ksize': PB({
+ 'list': PB({
+ "i": self.ksize
+ })
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ })
+ }})
+ self.expected = {
+ 'window': np.array(self.ksize, dtype=np.int8),
+ 'spatial_dims': [2, 3],
+ 'stride': np.array(self.strides, dtype=np.int8),
+ 'pool_method': "avg",
+ }
+ node = PB({'pb': pb})
+ AvgPoolFrontExtractor.extract(node)
+ self.res = node
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = (None, "avg")
+ self.compare()
+
+ def test_max_pool_nhwc(self):
+ pb = PB({'attr': {
+ 'data_format': PB({
+ 's': b"NHWC"
+ }),
+ 'strides': PB({
+ 'list': PB({
+ "i": self.strides
+ })
+ }),
+ 'ksize': PB({
+ 'list': PB({
+ "i": self.ksize
+ })
+ }),
+ 'padding': PB({
+ 's': b'VALID'
+ })
+ }})
+ self.expected = {
+ 'window': np.array(self.ksize, dtype=np.int8),
+ 'spatial_dims': [1, 2],
+ 'stride': np.array(self.strides, dtype=np.int64),
+ 'pool_method': "max",
+ }
+ node = PB({'pb': pb})
+ MaxPoolFrontExtractor.extract(node)
+ self.res = node
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = (None, "max")
+ self.compare()
diff --git a/model-optimizer/extensions/front/tf/stop_gradient_ext_test.py b/model-optimizer/extensions/front/tf/stop_gradient_ext_test.py
new file mode 100644
index 000000000..603039307
--- /dev/null
+++ b/model-optimizer/extensions/front/tf/stop_gradient_ext_test.py
@@ -0,0 +1,46 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+from extensions.front.tf.stop_gradient_ext import StopGradientExtractor
+from mo.utils.unittest.extractors import PB
+from generator import generator, generate
+
+
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class StopGradientTest(BaseExtractorsTestingClass):
+
+ def test_stop_gradient(self):
+ node = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'op': 'StopGradient'
+ }
+ StopGradientExtractor().extract(node)
+ self.res = node
+ self.compare()
+
diff --git a/model-optimizer/extensions/middle/AddIsCyclicAttribute_test.py b/model-optimizer/extensions/middle/AddIsCyclicAttribute_test.py
new file mode 100644
index 000000000..81f4ba74a
--- /dev/null
+++ b/model-optimizer/extensions/middle/AddIsCyclicAttribute_test.py
@@ -0,0 +1,50 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.middle.AddIsCyclicAttribute import AddIsCyclicAttribute
+from mo.utils.unittest.graph import build_graph_with_attrs
+
+
+class AddIsCyclicAttributeTest(unittest.TestCase):
+ nodes = [('node_1', {}),
+ ('node_2', {})]
+ edges = [('node_1', 'node_2')]
+
+ def test_1(self):
+ """
+ Acyclic case => graph.graph['is_cyclic'] should be False.
+ """
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges)
+ tested_pass = AddIsCyclicAttribute()
+ tested_pass.find_and_replace_pattern(graph)
+
+ assert graph.graph['is_cyclic'] is False
+
+ def test_2(self):
+ """
+ Cyclic case => graph.graph['is_cyclic'] should be True.
+ :return:
+ """
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ new_edges_with_attrs=[('node_2', 'node_1')])
+ tested_pass = AddIsCyclicAttribute()
+ tested_pass.find_and_replace_pattern(graph)
+
+ assert graph.graph['is_cyclic'] is True
diff --git a/model-optimizer/extensions/middle/AddReshapeAfterStridedSlice_test.py b/model-optimizer/extensions/middle/AddReshapeAfterStridedSlice_test.py
new file mode 100644
index 000000000..a834d996b
--- /dev/null
+++ b/model-optimizer/extensions/middle/AddReshapeAfterStridedSlice_test.py
@@ -0,0 +1,312 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+import unittest
+
+from extensions.middle.AddReshapeAfterStridedSlice import AddReshapeAfterStridedSlice
+from mo.graph.graph import Node
+from mo.middle.passes.fusing.fuse_linear_ops_test import compare_graphs
+from mo.middle.passes.eliminate_test import build_graph
+
+# The dictionary with nodes attributes used to build various graphs. A key is the name of the node and the value is the
+# dictionary with node attributes.
+nodes_attributes_test = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_2': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_begin_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_end_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_stride_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ # StridedSlice layers
+ 'sslice_1': {'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice', 'slices': None,
+ 'shrink_axis_mask': np.array([False, False, True, False]),
+ 'new_axis_mask': np.array([False, False, False, False])},
+ 'sslice_1_data': {'shape': None, 'kind': 'data'},
+ 'sslice_2': {'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice', 'slices': None,
+ 'shrink_axis_mask': np.array([False, False, True, False]),
+ 'new_axis_mask': np.array([False, False, False, False])},
+ 'sslice_2_data': {'shape': None, 'kind': 'data'}}
+
+nodes_reshape = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_2': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_begin_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_end_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_stride_data': {'shape': None, 'kind': 'data', 'data_type': None},
+ # StridedSlice layers
+ 'sslice_1': {'type': 'StridedSlice', 'value': None, 'kind': 'op', 'op': 'StridedSlice', 'slices': None,
+ 'shrink_axis_mask': np.array([False, False, True, False]),
+ 'new_axis_mask': np.array([False, False, False, False])},
+ 'sslice_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'sslice_2': {'type': 'StridedSlice', 'value': None, 'kind': 'op', 'op': 'StridedSlice', 'slices': None,
+ 'shrink_axis_mask': np.array([False, False, True, False]),
+ 'new_axis_mask': np.array([False, False, False, False])},
+ 'sslice_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Reshape layer
+ 'sslice_1/Reshape_shrink': {'type': 'Reshape', 'value': None, 'kind': 'op', 'op': 'Reshape'},
+ 'sslice_1/Reshape_shrink_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'sslice_2/Reshape_shrink': {'type': 'Reshape', 'value': None, 'kind': 'op', 'op': 'Reshape'},
+ 'sslice_2/Reshape_shrink_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'sslice_2/Reshape_new': {'type': 'Reshape', 'value': None, 'kind': 'op', 'op': 'Reshape'},
+ 'sslice_2/Reshape_new_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class AddReshapeAfterStridedSliceTests(unittest.TestCase):
+ def test_ss_1_shrink_last(self):
+ graph = build_graph(nodes_attributes_test,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('placeholder_begin_data', 'sslice_1'),
+ ('placeholder_end_data', 'sslice_1'),
+ ('placeholder_stride_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 1, 1), slice(0, 54, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 54]), 'is_output': True},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_reshape,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('placeholder_begin_data', 'sslice_1'),
+ ('placeholder_end_data', 'sslice_1'),
+ ('placeholder_stride_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1/Reshape_shrink_data'),
+ ('sslice_1/Reshape_shrink_data', 'sslice_1/Reshape_shrink'),
+ ('sslice_1/Reshape_shrink', 'sslice_1_data')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 1, 1), slice(0, 54, 1)]),
+ 'shrink_axis_mask': np.array([False, False, False, False]),
+ 'new_axis_mask': np.array([False, False, False, False])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 54]), 'is_output': True},
+ 'sslice_1/Reshape_shrink': {'dim': np.array([1, 227, 54])},
+ 'sslice_1/Reshape_shrink_data': {'shape': np.array([1, 227, 1, 54])}
+ })
+
+ pattern = AddReshapeAfterStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'sslice_1_data', check_op_attrs=True)
+ graph.clear()
+ graph_ref.clear()
+ self.assertTrue(flag, resp)
+
+ def test_ss_1_shrink(self):
+ graph = build_graph(nodes_attributes_test,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('placeholder_begin_data', 'sslice_2'),
+ ('placeholder_end_data', 'sslice_2'),
+ ('placeholder_stride_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('sslice_2_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'), ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 1, 1), slice(0, 54, 1)]), },
+ 'sslice_2_data': {'shape': np.array([1, 227, 54]), 'is_output': True}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_reshape,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('placeholder_begin_data', 'sslice_2'),
+ ('placeholder_end_data', 'sslice_2'),
+ ('placeholder_stride_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2/Reshape_shrink_data'),
+ ('sslice_2/Reshape_shrink_data', 'sslice_2/Reshape_shrink'),
+ ('sslice_2/Reshape_shrink', 'sslice_2_data'),
+ ('sslice_2_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 1, 1), slice(0, 54, 1)]),
+ 'shrink_axis_mask': np.array([False, False, False, False]),
+ 'new_axis_mask': np.array([False, False, False, False])},
+ 'sslice_2_data': {'shape': np.array([1, 227, 54])},
+ 'sslice_2/Reshape_shrink': {'dim': np.array([1, 227, 54])},
+ 'sslice_2/Reshape_shrink_data': {'shape': np.array([1, 227, 1, 54])},
+ })
+
+ pattern = AddReshapeAfterStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'sslice_2_data', check_op_attrs=True)
+ graph.clear()
+ graph_ref.clear()
+ self.assertTrue(flag, resp)
+
+ def test_ss_2_shrink(self):
+ graph = build_graph(nodes_attributes_test,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('placeholder_begin_data', 'sslice_2'),
+ ('placeholder_end_data', 'sslice_2'),
+ ('placeholder_stride_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('sslice_2_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'), ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_2': {
+ 'slices': np.array([slice(0, 1, 1), slice(0, 1, 1), slice(0, 227, 1), slice(0, 1, 1)]),
+ 'shrink_axis_mask': np.array([False, True, False, True])},
+ 'sslice_2_data': {'shape': np.array([1, 227]), 'is_output': True}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_reshape,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('placeholder_begin_data', 'sslice_2'),
+ ('placeholder_end_data', 'sslice_2'),
+ ('placeholder_stride_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2/Reshape_shrink_data'),
+ ('sslice_2/Reshape_shrink_data', 'sslice_2/Reshape_shrink'),
+ ('sslice_2/Reshape_shrink', 'sslice_2_data'),
+ ('sslice_2_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 1, 1), slice(0, 227, 1), slice(0, 1, 1)]),
+ 'shrink_axis_mask': np.array([False, False, False, False]),
+ 'new_axis_mask': np.array([False, False, False, False])},
+ 'sslice_2_data': {'shape': np.array([1, 227])},
+ 'sslice_2/Reshape_shrink': {'dim': np.array([1, 227])},
+ 'sslice_2/Reshape_shrink_data': {'shape': np.array([1, 1, 227, 1])},
+ })
+
+ pattern = AddReshapeAfterStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'sslice_2_data', check_op_attrs=True)
+ graph.clear()
+ graph_ref.clear()
+ self.assertTrue(flag, resp)
+
+ def test_ss_1_new(self):
+ graph = build_graph(nodes_attributes_test,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('placeholder_begin_data', 'sslice_2'),
+ ('placeholder_end_data', 'sslice_2'),
+ ('placeholder_stride_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('sslice_2_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'), ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(0, 54, 1)]),
+ 'shrink_axis_mask': np.array([False, False, False, False, False]),
+ 'new_axis_mask': np.array([False, True, False, False, False])},
+ 'sslice_2_data': {'shape': np.array([1, 1, 227, 227, 54])}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_reshape,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('placeholder_begin_data', 'sslice_2'),
+ ('placeholder_end_data', 'sslice_2'),
+ ('placeholder_stride_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2/Reshape_new_data'),
+ ('sslice_2/Reshape_new_data', 'sslice_2/Reshape_new'),
+ ('sslice_2/Reshape_new', 'sslice_2_data'),
+ ('sslice_2_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1),
+ slice(0, 54, 1)]),
+ 'shrink_axis_mask': np.array([False, False, False, False, False]),
+ 'new_axis_mask': np.array([False, False, False, False, False])},
+ 'sslice_2_data': {'shape': np.array([1, 1, 227, 227, 54])},
+ 'sslice_2/Reshape_new': {'dim': np.array([1, 1, 227, 227, 54])},
+ 'sslice_2/Reshape_new_data': {'shape': np.array([1, 227, 227, 54])},
+ })
+
+ pattern = AddReshapeAfterStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'sslice_2_data', check_op_attrs=True)
+ graph.clear()
+ graph_ref.clear()
+ self.assertTrue(flag, resp)
+
+ def test_ss_shrink_new(self):
+ graph = build_graph(nodes_attributes_test,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('placeholder_begin_data', 'sslice_2'),
+ ('placeholder_end_data', 'sslice_2'),
+ ('placeholder_stride_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('sslice_2_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'), ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 1, 1), slice(0, 227, 1), slice(0, 1, 1), slice(0, 54, 1)]),
+ 'shrink_axis_mask': np.array([False, False, False, True, False]),
+ 'new_axis_mask': np.array([False, True, False, False, False])},
+ 'sslice_2_data': {'shape': np.array([1, 1, 227, 54]), 'is_output': True}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_reshape,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('placeholder_begin_data', 'sslice_2'),
+ ('placeholder_end_data', 'sslice_2'),
+ ('placeholder_stride_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2/Reshape_new_data'),
+ ('sslice_2/Reshape_new_data', 'sslice_2/Reshape_new'),
+ ('sslice_2/Reshape_new', 'sslice_2/Reshape_shrink_data'),
+ ('sslice_2/Reshape_shrink_data', 'sslice_2/Reshape_shrink'),
+ ('sslice_2/Reshape_shrink', 'sslice_2_data'),
+ ('sslice_2_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 1, 1), slice(0, 227, 1), slice(0, 1, 1),
+ slice(0, 54, 1)]),
+ 'shrink_axis_mask': np.array([False, False, False, False, False]),
+ 'new_axis_mask': np.array([False, False, False, False, False])},
+ 'sslice_2_data': {'shape': np.array([1, 1, 227, 54])},
+ 'sslice_2/Reshape_new': {'dim': np.array([1, 1, 227, 1, 54])},
+ 'sslice_2/Reshape_new_data': {'shape': np.array([1, 227, 1, 54])},
+ 'sslice_2/Reshape_shrink': {'dim': np.array([1, 1, 227, 54])},
+ 'sslice_2/Reshape_shrink_data': {'shape': np.array([1, 1, 227, 1, 54])},
+ })
+
+ pattern = AddReshapeAfterStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'sslice_2_data', check_op_attrs=True)
+ graph.clear()
+ graph_ref.clear()
+ self.assertTrue(flag, resp)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/model-optimizer/extensions/middle/ConvertGroupedStridedSlice_test.py b/model-optimizer/extensions/middle/ConvertGroupedStridedSlice_test.py
new file mode 100644
index 000000000..0ebdb3876
--- /dev/null
+++ b/model-optimizer/extensions/middle/ConvertGroupedStridedSlice_test.py
@@ -0,0 +1,530 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.middle.ConvertGroupedStridedSlice import ConvertGroupedStridedSlice
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # StridedSlice layers
+ 'sslice_1': {'type': None, 'kind': 'op', 'op': 'StridedSlice', 'slices': None,
+ 'shrink_axis_mask': np.array([False, False, False, False])},
+ 'sslice_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'sslice_2': {'type': None, 'kind': 'op', 'op': 'StridedSlice', 'slices': None,
+ 'shrink_axis_mask': np.array([False, False, False, False])},
+ 'sslice_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'sslice_3': {'type': None, 'kind': 'op', 'op': 'StridedSlice', 'slices': None,
+ 'shrink_axis_mask': np.array([False, False, False, False])},
+ 'sslice_3_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Split layer
+ 'split_1': {'type': 'Split', 'kind': 'op', 'op': 'SplitV'},
+ 'split_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'split_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'split_3_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'split_4_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Concat1 operation
+ 'concat_1': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+ 'concat_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class ConvertGroupedStridedSliceTests(unittest.TestCase):
+ def test_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('placeholder_1_data', 'sslice_3'),
+ ('sslice_3', 'sslice_3_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('sslice_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(0, 18, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(18, 36, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'sslice_3': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(36, 54, 1)])},
+ 'sslice_3_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'split_1'),
+ ('split_1', 'split_1_data'),
+ ('split_1', 'split_2_data'),
+ ('split_1', 'split_3_data'),
+ ('split_1_data', 'concat_1'),
+ ('split_2_data', 'concat_1'),
+ ('split_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'split_1': {'axis': 3},
+ 'split_1_data': {'shape': np.array([1, 227, 227, 18])},
+ 'split_2_data': {'shape': np.array([1, 227, 227, 18])},
+ 'split_3_data': {'shape': np.array([1, 227, 227, 18])},
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+
+ pattern = ConvertGroupedStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('placeholder_1_data', 'sslice_3'),
+ ('sslice_3', 'sslice_3_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('sslice_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(19, 37, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(37, 54, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 227, 227, 17])},
+
+ 'sslice_3': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(0, 19, 1)])},
+ 'sslice_3_data': {'shape': np.array([1, 227, 227, 19])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'split_1'),
+ ('split_1', 'split_1_data'),
+ ('split_1', 'split_2_data'),
+ ('split_1', 'split_3_data'),
+ ('split_1_data', 'concat_1'),
+ ('split_2_data', 'concat_1'),
+ ('split_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'split_1': {'axis': 3},
+ 'split_1_data': {'shape': np.array([1, 227, 227, 18])},
+ 'split_2_data': {'shape': np.array([1, 227, 227, 17])},
+ 'split_3_data': {'shape': np.array([1, 227, 227, 19])},
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+
+ pattern = ConvertGroupedStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Intersection of split ranges in feature dimension
+ def test_3_neg(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('placeholder_1_data', 'sslice_3'),
+ ('sslice_3', 'sslice_3_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('sslice_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(19, 39, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 227, 20])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(37, 54, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 227, 227, 17])},
+
+ 'sslice_3': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(0, 19, 1)])},
+ 'sslice_3_data': {'shape': np.array([1, 227, 227, 19])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('placeholder_1_data', 'sslice_3'),
+ ('sslice_3', 'sslice_3_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('sslice_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(19, 39, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 227, 20])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(37, 54, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 227, 227, 17])},
+
+ 'sslice_3': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(0, 19, 1)])},
+ 'sslice_3_data': {'shape': np.array([1, 227, 227, 19])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+
+ pattern = ConvertGroupedStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Split range overflow in feature dimension
+ def test_4_neg(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('placeholder_1_data', 'sslice_3'),
+ ('sslice_3', 'sslice_3_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('sslice_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(19, 37, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(37, 55, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'sslice_3': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(0, 19, 1)])},
+ 'sslice_3_data': {'shape': np.array([1, 227, 227, 19])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('placeholder_1_data', 'sslice_3'),
+ ('sslice_3', 'sslice_3_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('sslice_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(19, 37, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(37, 55, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'sslice_3': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(0, 19, 1)])},
+ 'sslice_3_data': {'shape': np.array([1, 227, 227, 19])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+
+ pattern = ConvertGroupedStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Split(1,H,W,54)--->Fake_data (1,H,W,1)
+ # |`---->Sslice1_out (1,H,W,18)
+ # |`---->Sslice2_out (1,H,W,18)
+ # `----->Sslice3_out (1,H,W,17)
+ def test_5(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('placeholder_1_data', 'sslice_3'),
+ ('sslice_3', 'sslice_3_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('sslice_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(19, 37, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(37, 54, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 227, 227, 17])},
+
+ 'sslice_3': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(1, 19, 1)])},
+ 'sslice_3_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'split_1'),
+ ('split_1', 'split_1_data'),
+ ('split_1', 'split_2_data'),
+ ('split_1', 'split_3_data'),
+ ('split_1', 'split_4_data'),
+ ('split_2_data', 'concat_1'),
+ ('split_3_data', 'concat_1'),
+ ('split_4_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'split_1': {'axis': 3},
+ 'split_1_data': {'shape': np.array([1, 227, 227, 1])},
+ 'split_2_data': {'shape': np.array([1, 227, 227, 18])},
+ 'split_3_data': {'shape': np.array([1, 227, 227, 17])},
+ 'split_4_data': {'shape': np.array([1, 227, 227, 18])},
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+
+ pattern = ConvertGroupedStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Split(1,H,W,54)
+ # |`---->Sslice1_out (1,H,W,(0,18))
+ # |`---->Fake_data (1,H,W,(18,27))
+ # |`---->Sslice3_out (1,H,W,(27,45))
+ # `----->Fake_data (1,H,W,(45,54))
+ def test_6(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(0, 18, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(27, 45, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 227, 227, 18])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'split_1'),
+ ('split_1', 'split_1_data'),
+ ('split_1', 'split_2_data'),
+ ('split_1', 'split_3_data'),
+ ('split_1', 'split_4_data'),
+ ('split_1_data', 'concat_1'),
+ ('split_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+ 'split_1': {'axis': 3},
+ 'split_1_data': {'shape': np.array([1, 227, 227, 18])},
+ 'split_2_data': {'shape': np.array([1, 227, 227, 9])},
+ 'split_3_data': {'shape': np.array([1, 227, 227, 18])},
+ 'split_4_data': {'shape': np.array([1, 227, 227, 9])},
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+
+ pattern = ConvertGroupedStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_7_neg(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 10, 1), slice(0, 227, 1), slice(0, 18, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 10, 227, 18])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(10, 227, 1), slice(0, 227, 1), slice(27, 45, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 217, 227, 18])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 54])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 10, 1), slice(0, 227, 1), slice(0, 18, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 10, 227, 18])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(10, 227, 1), slice(0, 227, 1), slice(27, 45, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 217, 227, 18])},
+
+ 'concat_1_data': {'shape': np.array([1, 227, 227, 54]), 'is_output': True},
+ })
+
+ pattern = ConvertGroupedStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Split(1,54,W,C)
+ # |`---->Sslice1_out (1,(0,18),W,C)
+ # |`---->Sslice2_out (1,(18,36),W,C)
+ # `----->Fake_data (1,(36,54),W,C)
+ def test_8(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'sslice_1'),
+ ('sslice_1', 'sslice_1_data'),
+ ('placeholder_1_data', 'sslice_2'),
+ ('sslice_2', 'sslice_2_data'),
+ ('sslice_1_data', 'concat_1'),
+ ('sslice_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 54, 54, 3])},
+
+ 'sslice_1': {'slices': np.array(
+ [slice(0, 1, 1), slice(0, 18, 1), slice(0, 54, 1), slice(0, 3, 1)])},
+ 'sslice_1_data': {'shape': np.array([1, 18, 54, 3])},
+
+ 'sslice_2': {'slices': np.array(
+ [slice(0, 1, 1), slice(18, 36, 1), slice(0, 54, 1), slice(0, 3, 1)])},
+ 'sslice_2_data': {'shape': np.array([1, 18, 54, 3])},
+
+ 'concat_1_data': {'shape': np.array([1, 54, 54, 3]), 'is_output': True},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'split_1'),
+ ('split_1', 'split_1_data'),
+ ('split_1', 'split_2_data'),
+ ('split_1', 'split_3_data'),
+ ('split_1_data', 'concat_1'),
+ ('split_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 54, 54, 3])},
+ 'split_1': {'axis': 1},
+ 'split_1_data': {'shape': np.array([1, 18, 54, 3])},
+ 'split_2_data': {'shape': np.array([1, 18, 54, 3])},
+ 'split_3_data': {'shape': np.array([1, 18, 54, 3])},
+ 'concat_1_data': {'shape': np.array([1, 54, 54, 3]), 'is_output': True},
+ })
+
+ pattern = ConvertGroupedStridedSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/model-optimizer/extensions/middle/EltwiseInputNormalization_test.py b/model-optimizer/extensions/middle/EltwiseInputNormalization_test.py
new file mode 100644
index 000000000..829b13be6
--- /dev/null
+++ b/model-optimizer/extensions/middle/EltwiseInputNormalization_test.py
@@ -0,0 +1,178 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.middle.EltwiseInputNormalization import EltwiseInputNormalize
+from extensions.middle.EltwiseInputReshape import EltwiseInputReshape
+from mo.middle.passes.eliminate_test import build_graph
+from mo.middle.passes.fusing.fuse_linear_ops_test import compare_graphs
+
+# The dictionary with nodes attributes used to build various graphs. A key is the name of the node and the value is the
+# dictionary with node attributes.
+nodes_attributes = {
+ # Placeholder layers
+ 'placeholder_1': {'value': None, 'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_4_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+
+ # Reshape layers
+ 'reshape_1': {'type': 'Reshape', 'value': None, 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'reshape_2': {'type': 'Reshape', 'value': None, 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ # Eltwise consumes layers
+ 'eltwise_1': {'type': 'Eltwise', 'value': None, 'kind': 'op', 'op': 'Eltwise'},
+ 'eltwise_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'eltwise_2': {'type': 'Eltwise', 'value': None, 'kind': 'op', 'op': 'Eltwise'},
+ 'eltwise_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'eltwise_3': {'type': 'Eltwise', 'value': None, 'kind': 'op', 'op': 'Eltwise'},
+ 'eltwise_3_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'eltwise_4': {'type': 'Eltwise', 'value': None, 'kind': 'op', 'op': 'Eltwise'},
+ 'eltwise_4_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ # Concat
+ 'concat': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+}
+
+
+class EltwiseInputNormalizationTest(unittest.TestCase):
+ def test1_not_constant(self):
+ #
+ # data1(1,3,64,64)----. data(1,3,64,64)-------.
+ # data2(1,64,1)-------->Eltwise-->data(1,3,64,64) => data(1,64,1)->Reshape->data(1,1,64,1)-->Eltwise->...
+ # data3(64,1)------' data(64,1)->Reshape->data(1,1,64,1)-'
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'eltwise_1'),
+ ('placeholder_2_data', 'eltwise_1'),
+ ('placeholder_3_data', 'eltwise_1'),
+ ('eltwise_1', 'eltwise_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'placeholder_2_data': {'shape': np.array([1, 64, 1])},
+ 'placeholder_3_data': {'shape': np.array([64, 1])},
+ 'eltwise_1_data': {'shape': np.array([1, 3, 64, 64])}
+ }, nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'eltwise_1'),
+ ('placeholder_2_data', 'reshape_1'),
+ ('placeholder_3_data', 'reshape_2'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_1_data', 'eltwise_1'),
+ ('reshape_2_data', 'eltwise_1'),
+ ('eltwise_1', 'eltwise_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'reshape_1': {'dim': np.array([1, 1, 64, 1])},
+ 'reshape_1_data': {'shape': np.array([1, 1, 64, 1])},
+ 'reshape_2': {'dim': np.array([1, 1, 64, 1])},
+ 'reshape_2_data': {'shape': np.array([1, 1, 64, 1])},
+ 'eltwise_1_data': {'shape': np.array([1, 3, 64, 64])}
+ }, nodes_with_edges_only=True)
+
+ pattern = EltwiseInputNormalize()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'eltwise_1', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_mega_hardcore(self):
+ # ORIGINAL GRAPH
+ #
+ # data1(1,3,64,64)---,->Eltwise1->data(1,3,64,64)-----,->Eltwise2->data(1,3,64,64)---,->Eltwise4->data(1,3,64,64)
+ # /\ /\ /\
+ # data2(64,1)-----,-'--------------------------------'------------------------------'
+ # \/ /
+ # data3(64,1)----`-->Eltwise3->data(64,1)----------'
+ #
+ # REFERENCE GRAPH AFTER TRANSFORMATION
+ #
+ # data1(1,3,64,64)---,->Eltwise1->data(1,3,64,64)-----,->Eltwise2->data(1,3,64,64)---,->Eltwise4->data(1,3,64,64)
+ # /\ /\ /\
+ # data2(1,1,64,1)---'--------------------------------'-------------------------------'
+ # /
+ # data4(64,1)-------, Reshape(1,1,64,1)
+ # \/ |
+ # data3(64,1)------`---->Eltwise3->data(64,1)---'
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'eltwise_1'),
+ ('placeholder_2_data', 'eltwise_1'),
+ ('eltwise_1', 'eltwise_1_data'),
+ ('eltwise_1_data', 'eltwise_2'),
+ ('placeholder_2_data', 'eltwise_3'),
+ ('placeholder_3_data', 'eltwise_3'),
+ ('eltwise_3', 'eltwise_3_data'),
+ ('eltwise_3_data', 'eltwise_2'),
+ ('eltwise_2', 'eltwise_2_data'),
+ ('eltwise_2_data', 'eltwise_4'),
+ ('placeholder_2_data', 'eltwise_4'),
+ ('eltwise_4', 'eltwise_4_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'placeholder_2_data': {'shape': np.array([64, 1]), 'value': np.ones([64, 1])},
+ 'placeholder_3_data': {'shape': np.array([64, 1])},
+ 'eltwise_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'eltwise_2_data': {'shape': np.array([1, 3, 64, 64])},
+ 'eltwise_3_data': {'shape': np.array([64, 1])},
+ 'eltwise_4_data': {'shape': np.array([1, 3, 64, 64])}
+ }, nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'eltwise_1'),
+ ('placeholder_2_data', 'eltwise_1'),
+ ('eltwise_1', 'eltwise_1_data'),
+ ('eltwise_1_data', 'eltwise_2'),
+ ('placeholder_4_data', 'eltwise_3'),
+ ('placeholder_3_data', 'eltwise_3'),
+ ('eltwise_3', 'eltwise_3_data'),
+ ('eltwise_3_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'eltwise_2'),
+ ('eltwise_2', 'eltwise_2_data'),
+ ('eltwise_2_data', 'eltwise_4'),
+ ('placeholder_2_data', 'eltwise_4'),
+ ('eltwise_4', 'eltwise_4_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'placeholder_2_data': {'shape': np.array([1, 1, 64, 1]), 'value': np.ones([1, 1, 64, 1])},
+ 'placeholder_3_data': {'shape': np.array([64, 1])},
+ 'placeholder_4_data': {'shape': np.array([64, 1]), 'value': np.ones([64, 1])},
+ 'reshape_1': {'dim': np.array([1,1,64,1])},
+ 'reshape_1_data': {'shape': np.array([1,1,64,1])},
+ 'eltwise_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'eltwise_2_data': {'shape': np.array([1, 3, 64, 64])},
+ 'eltwise_3_data': {'shape': np.array([64, 1])},
+ 'eltwise_4_data': {'shape': np.array([1, 3, 64, 64])}
+ }, nodes_with_edges_only=True)
+
+ pattern = EltwiseInputNormalize()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'eltwise_1', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/EltwiseInputReshape_test.py b/model-optimizer/extensions/middle/EltwiseInputReshape_test.py
new file mode 100644
index 000000000..24c727d72
--- /dev/null
+++ b/model-optimizer/extensions/middle/EltwiseInputReshape_test.py
@@ -0,0 +1,225 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.middle.EltwiseInputReshape import EltwiseInputReshape
+from mo.middle.passes.eliminate_test import build_graph
+from mo.middle.passes.fusing.fuse_linear_ops_test import compare_graphs
+
+# The dictionary with nodes attributes used to build various graphs. A key is the name of the node and the value is the
+# dictionary with node attributes.
+nodes_attributes = {
+ # Placeholder layers
+ 'placeholder_1': {'value': None, 'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+
+ # Reshape layers
+ 'reshape_1': {'type': 'Reshape', 'value': None, 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'reshape_2': {'type': 'Reshape', 'value': None, 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ # Fake consumes layers
+ 'consumer_1': {'type': 'Consumer', 'value': None, 'kind': 'op', 'op': 'Consumer'},
+ 'consumer_2': {'type': 'Consumer', 'value': None, 'kind': 'op', 'op': 'Consumer'},
+ 'consumer_3': {'type': 'Consumer', 'value': None, 'kind': 'op', 'op': 'Consumer'},
+
+ # Concat
+ 'concat': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+}
+
+
+class EltwiseInputReshapeTest(unittest.TestCase):
+ def test1_not_constant(self):
+ # ,-------------->consumer3 ,------------>consumer3
+ # data---(new_shape1)-->consumer1 => data---->Reshape-->consumer1
+ # `-(new_shape2)-->consumer2 `-->Reshape-->consumer2
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'consumer_1', {'new_shape': [1, 3, 1, 1]}),
+ ('placeholder_1_data', 'consumer_2', {'new_shape': [1, 1, 3]}),
+ ('placeholder_1_data', 'consumer_3'),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3])}}, nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reshape_1'),
+ ('placeholder_1_data', 'reshape_2'),
+ ('placeholder_1_data', 'consumer_3'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_1_data', 'consumer_1'),
+ ('reshape_2_data', 'consumer_2'),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3])},
+ 'reshape_1': {'dim': np.array([1, 3, 1, 1])},
+ 'reshape_1_data': {'shape': np.array([1, 3, 1, 1])},
+ 'reshape_2': {'dim': np.array([1, 1, 3])},
+ 'reshape_2_data': {'shape': np.array([1, 1, 3])},
+ }, nodes_with_edges_only=True)
+
+ pattern = EltwiseInputReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test2_not_constant(self):
+ # ,--------------->consumer3 ,----------->consumer3
+ # data---(new_shape1)-->consumer1 => data-->Reshape-->consumer1
+ # `-(new_shape1)-->consumer2 `-->consumer2
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'consumer_1', {'new_shape': [1, 3, 1, 1]}),
+ ('placeholder_1_data', 'consumer_2', {'new_shape': [1, 3, 1, 1]}),
+ ('placeholder_1_data', 'consumer_3'),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3])}}, nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reshape_1'),
+ ('placeholder_1_data', 'consumer_3'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'consumer_1'),
+ ('reshape_1_data', 'consumer_2'),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3])},
+ 'reshape_1': {'dim': np.array([1, 3, 1, 1])},
+ 'reshape_1_data': {'shape': np.array([1, 3, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ pattern = EltwiseInputReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test3_constant(self):
+ # ,--------------->consumer3 data-->consumer3
+ # data---(new_shape1)-->consumer1 => data-->consumer1
+ # `-(new_shape2)-->consumer2 data-->consumer2
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'consumer_1', {'new_shape': [1, 3, 1, 1]}),
+ ('placeholder_1_data', 'consumer_2', {'new_shape': [1, 1, 3]}),
+ ('placeholder_1_data', 'consumer_3'),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3]), 'value': np.ones([1, 3])}},
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'consumer_1'),
+ ('placeholder_2_data', 'consumer_2'),
+ ('placeholder_3_data', 'consumer_3'),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 1, 1]), 'value': np.ones([1, 3, 1, 1])},
+ 'placeholder_2_data': {'shape': np.array([1, 1, 3]), 'value': np.ones([1, 1, 3])},
+ 'placeholder_3_data': {'shape': np.array([1, 3]), 'value': np.ones([1, 3])},
+ }, nodes_with_edges_only=True)
+
+ pattern = EltwiseInputReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test4_constant(self):
+ # ,--------------->consumer3 ,-->consumer3
+ # data---(new_shape1)-->consumer1 => data-->consumer1
+ # `-(new_shape2)-->consumer2 `->consumer2
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'consumer_1', {'new_shape': [3, 1, 1]}),
+ ('placeholder_1_data', 'consumer_2', {'new_shape': [3, 1, 1]}),
+ ('placeholder_1_data', 'consumer_3', {'new_shape': [3, 1, 1]}),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3]), 'value': np.ones([1, 3])}},
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'consumer_1'),
+ ('placeholder_1_data', 'consumer_2'),
+ ('placeholder_1_data', 'consumer_3'),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([3, 1, 1]), 'value': np.ones([3, 1, 1])}
+ }, nodes_with_edges_only=True)
+
+ pattern = EltwiseInputReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test5_not_constant(self):
+ # ,--------------->consumer3 ,->consumer3
+ # data---(new_shape1)-->consumer1 => data----->consumer1
+ # `-(new_shape1)-->consumer2 `-->consumer2
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'consumer_1', {'new_shape': [1, 3]}),
+ ('placeholder_1_data', 'consumer_2', {'new_shape': [1, 3]}),
+ ('placeholder_1_data', 'consumer_3'),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3])}}, nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'consumer_1', {'new_shape': [1, 3]}),
+ ('placeholder_1_data', 'consumer_2', {'new_shape': [1, 3]}),
+ ('placeholder_1_data', 'consumer_3'),
+ ('consumer_1', 'concat'),
+ ('consumer_2', 'concat'),
+ ('consumer_3', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3])}}, nodes_with_edges_only=True)
+
+ pattern = EltwiseInputReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/FusePermutesSequence_test.py b/model-optimizer/extensions/middle/FusePermutesSequence_test.py
new file mode 100644
index 000000000..850cf1740
--- /dev/null
+++ b/model-optimizer/extensions/middle/FusePermutesSequence_test.py
@@ -0,0 +1,118 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.middle.FusePermutesSequence import FusePermutesSequence
+from mo.middle.passes.eliminate_test import build_graph
+from mo.middle.passes.fusing.fuse_linear_ops_test import compare_graphs
+
+# The dictionary with nodes attributes used to build various graphs. A key is the name of the node and the value is the
+# dictionary with node attributes.
+nodes_attributes = {
+ 'placeholder_1': {'name': 'placeholder_1', 'value': None, 'shape': None, 'type': 'Placeholder', 'kind': 'op',
+ 'op': 'Placeholder'},
+ 'placeholder_1_data': {'name': 'placeholder_1_data', 'value': None, 'shape': None, 'kind': 'data',
+ 'data_type': None},
+ # Permute layers
+ 'permute_1': {'type': 'Permute', 'value': None, 'kind': 'op', 'op': 'Permute'},
+ 'permute_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'permute_2': {'type': 'Permute', 'value': None, 'kind': 'op', 'op': 'Permute'},
+ 'permute_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'permute_3': {'type': 'Permute', 'value': None, 'kind': 'op', 'op': 'Permute'},
+ 'permute_3_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class FusePermutesSequenceTest(unittest.TestCase):
+ def test_1(self):
+ #
+ # NHWC NCHW NHWC
+ # Input->DATA->Permute->DATA->Permute->DATA => Input->DATA
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'permute_1'),
+ ('permute_1', 'permute_1_data'),
+ ('permute_1_data', 'permute_2'),
+ ('permute_2', 'permute_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+
+ 'permute_1': {'order': np.array([0, 3, 1, 2])},
+ 'permute_1_data': {'shape': np.array([1, 3, 227, 227])},
+
+ 'permute_2': {'order': np.array([0, 2, 3, 1])},
+ 'permute_2_data': {'shape': np.array([1, 227, 227, 3]), 'is_output': True},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])}}, nodes_with_edges_only=True)
+
+ pattern = FusePermutesSequence()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'placeholder_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_2(self):
+ #
+ # Input->DATA->Permute->DATA->Permute->DATA => Input->DATA->Permute->DATA
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'permute_1'),
+ ('permute_1', 'permute_1_data'),
+ ('permute_1_data', 'permute_2'),
+ ('permute_2', 'permute_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+
+ 'permute_1': {'order': np.array([0, 3, 1, 2])},
+ 'permute_1_data': {'shape': np.array([1, 3, 227, 227])},
+
+ 'permute_2': {'order': np.array([0, 1, 2, 3])},
+ 'permute_2_data': {'shape': np.array([1, 3, 227, 227]), 'is_output': True},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'permute_1'),
+ ('permute_1', 'permute_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'permute_1': {'order': np.array([0, 3, 1, 2])},
+ 'permute_1_data': {'shape': np.array([1, 3, 227, 227])},
+ }, nodes_with_edges_only=True)
+
+ pattern = FusePermutesSequence()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'placeholder_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/model-optimizer/extensions/middle/MinumumMiddleReplacer_test.py b/model-optimizer/extensions/middle/MinumumMiddleReplacer_test.py
new file mode 100644
index 000000000..eb04cda95
--- /dev/null
+++ b/model-optimizer/extensions/middle/MinumumMiddleReplacer_test.py
@@ -0,0 +1,84 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.middle.MinimumMiddleReplacer import MinimumMiddleReplacer
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+
+ 'placeholder_2': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # minimum node:
+ 'minimum': {'type': 'Minimum', 'kind': 'op', 'op': 'Minimum'},
+ # negates
+ 'negate_1': {'type': 'Power', 'kind': 'op', 'op': 'Power', 'power': 1, 'scale': -1, 'shift': 0},
+ 'negate_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+
+ 'negate_2': {'type': 'Power', 'kind': 'op', 'op': 'Power', 'power': 1, 'scale': -1, 'shift': 0},
+ 'negate_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+
+ 'negate_output': {'type': 'Power', 'kind': 'op', 'op': 'Power', 'power': 1, 'scale': -1, 'shift': 0},
+ 'negate_output_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+
+ # Maximum
+ 'maximum': {'type': 'Eltwise', 'kind': 'op', 'op': 'Max'},
+ 'maximum_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # output
+ 'output_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+}
+
+
+class MinumumMiddleReplacer_test(unittest.TestCase):
+ def test_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_1_data', 'minimum'),
+ ('placeholder_2_data', 'minimum'),
+ ('minimum', 'output_data')
+ ],
+ {'placeholder_1_data': {'value': 3, 'shape': np.array([])},
+ 'placeholder_2_data': {'value': None, 'shape': np.array([5, 5])},
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_1_data', 'negate_1'),
+ ('placeholder_2_data', 'negate_2'),
+ ('negate_1', 'negate_1_data'),
+ ('negate_2', 'negate_2_data'),
+ ('negate_1_data', 'maximum'),
+ ('negate_2_data', 'maximum'),
+ ('maximum', 'maximum_data'),
+ ('maximum_data', 'negate_output'),
+ ('negate_output', 'negate_output_data')
+ ])
+
+ graph.graph['layout'] = 'NHWC'
+
+ tested_class = MinimumMiddleReplacer()
+ tested_class.find_and_replace_pattern(graph=graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'minimum/negate_out_', last_node_ref='negate_output',
+ check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/NormalizeFullyConnected_test.py b/model-optimizer/extensions/middle/NormalizeFullyConnected_test.py
new file mode 100644
index 000000000..de6a73a8d
--- /dev/null
+++ b/model-optimizer/extensions/middle/NormalizeFullyConnected_test.py
@@ -0,0 +1,119 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.middle.FusePermutesSequence import FusePermutesSequence
+from extensions.middle.NormalizeFullyConnected import NormalizeFullyConnected
+from mo.middle.passes.eliminate_test import build_graph
+from mo.middle.passes.fusing.fuse_linear_ops_test import compare_graphs
+
+# The dictionary with nodes attributes used to build various graphs. A key is the name of the node and the value is the
+# dictionary with node attributes.
+nodes_attributes = {
+ 'placeholder_1': {'name': 'placeholder_1', 'value': None, 'shape': None, 'type': 'Placeholder', 'kind': 'op',
+ 'op': 'Placeholder'},
+ 'placeholder_1_data': {'name': 'placeholder_1_data', 'value': None, 'shape': None, 'kind': 'data',
+ 'data_type': None},
+ 'reshape_1': {'type': 'Reshape', 'value': None, 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'fc': {'type': 'FullyConnected', 'value': None, 'kind': 'op', 'op': 'MatMul'},
+ 'fc_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc_weights': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'reshape_2': {'type': 'Reshape', 'value': None, 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class NormalizeFullyConnectedTest(unittest.TestCase):
+ def test_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc'),
+ ('fc_weights', 'fc'),
+ ('fc', 'fc_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 16, 512])},
+ 'fc': {'out-size': 101},
+ 'fc_weights': {'shape': np.array([512,101]), 'value': np.ones([512, 101]), 'input_channel_dim': 1},
+ 'fc_data': {'shape': np.array([1, 16, 101])},
+ }, nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'fc'),
+ ('fc_weights', 'fc'),
+ ('fc', 'fc_data'),
+ ('fc_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 16, 512])},
+ 'reshape_1_data': {'shape': np.array([16, 512])},
+ 'reshape_2_data': {'shape': np.array([1, 16, 101])},
+ 'fc_weights': {'shape': np.array([512,101]), 'value': np.ones([512, 101])},
+ 'fc': {'out-size': 101},
+ 'fc_data': {'shape': np.array([16, 101])},
+ }, nodes_with_edges_only=True)
+
+ pattern = NormalizeFullyConnected()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'placeholder_1_data', 'placeholder_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+
+ def test_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc'),
+ ('fc_weights', 'fc'),
+ ('fc', 'fc_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([2, 32, 16, 512])},
+ 'fc': {'out-size': 101},
+ 'fc_weights': {'shape': np.array([512,101]), 'value': np.ones([512, 101]), 'input_channel_dim': 1},
+ 'fc_data': {'shape': np.array([2, 32, 16, 101])},
+ }, nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'fc'),
+ ('fc_weights', 'fc'),
+ ('fc', 'fc_data'),
+ ('fc_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([2, 32, 16, 512])},
+ 'reshape_1_data': {'shape': np.array([2 * 32 * 16, 512])},
+ 'reshape_2_data': {'shape': np.array([2, 32, 16, 101])},
+ 'fc_weights': {'shape': np.array([512,101]), 'value': np.ones([512, 101])},
+ 'fc': {'out-size': 101},
+ 'fc_data': {'shape': np.array([2 * 32 * 16, 101])},
+ }, nodes_with_edges_only=True)
+
+ pattern = NormalizeFullyConnected()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'placeholder_1_data', 'placeholder_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/PixelLinkReshape_test.py b/model-optimizer/extensions/middle/PixelLinkReshape_test.py
new file mode 100644
index 000000000..e281f6016
--- /dev/null
+++ b/model-optimizer/extensions/middle/PixelLinkReshape_test.py
@@ -0,0 +1,110 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.middle.PixelLinkReshape import PixelLinkReshape
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Reshape layers
+ 'reshape_pack': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_pack_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'reshape_split': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_split_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'reshape_unpack': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_unpack_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'strided_slice': {'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice'},
+ 'strided_slice_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Transpose layer
+ 'reshape_split/Permute_before': {'type': 'Permute', 'kind': 'op', 'op': 'Permute'},
+ 'reshape_split/Permute_before_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'reshape_pack/Permute_after': {'type': 'Permute', 'kind': 'op', 'op': 'Permute'},
+ 'reshape_pack/Permute_after_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Softmax layer
+ 'softmax_1': {'type': 'SoftMax', 'kind': 'op', 'op': 'SoftMax'},
+ 'softmax_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class ReshapeSoftmaxReshapeTests(unittest.TestCase):
+ def test_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_split'),
+ ('reshape_split', 'reshape_split_data'),
+ ('reshape_split_data', 'reshape_pack'),
+ ('reshape_pack', 'reshape_pack_data'),
+ ('reshape_pack_data', 'softmax_1'),
+ ('softmax_1', 'softmax_1_data'),
+ ('softmax_1_data', 'reshape_unpack'),
+ ('reshape_unpack', 'reshape_unpack_data'),
+ ('reshape_unpack_data', 'strided_slice'),
+ ('strided_slice', 'strided_slice_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 16])},
+ 'reshape_split': {'dim': np.array([1, 227, 227, 8, 2])},
+ 'reshape_split_data': {'shape': np.array([1, 227, 227, 8, 2])},
+ 'softmax_1_data': {'shape': np.array([1 * 227 * 227 * 8, 2])},
+ 'reshape_pack': {'dim': np.array([1 * 227 * 227 * 8, 2])},
+ 'reshape_pack_data': {'shape': np.array([1 * 227 * 227 * 8, 2])},
+ 'reshape_unpack': {'dim': np.array([1, 227, 227, 8, 2])},
+ 'reshape_unpack_data': {'shape': np.array([1, 227, 227, 8, 2])},
+ 'strided_slice': {
+ 'slices': [slice(0, 1, 1), slice(0, 227, 1), slice(0, 227, 1), slice(0, 8, 1),
+ slice(1, 2, 1)],
+ 'shrink_axis_mask': [False, False, False, False, True],
+ 'new_axis_mask': [False, False, False, False, False]},
+ 'strided_slice_data': {'shape': np.array([1, 227, 227, 8])},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_split/Permute_before'),
+ ('reshape_split/Permute_before', 'reshape_split/Permute_before_data'),
+ ('reshape_split/Permute_before_data', 'reshape_split'),
+ ('reshape_split', 'reshape_split_data'),
+ ('reshape_split_data', 'reshape_pack'),
+ ('reshape_pack', 'reshape_pack/Permute_after_data'),
+ ('reshape_pack/Permute_after_data', 'reshape_pack/Permute_after'),
+ ('reshape_pack/Permute_after', 'reshape_pack_data'),
+ ('reshape_pack_data', 'softmax_1'),
+ ('softmax_1', 'softmax_1_data'),
+ ('softmax_1_data', 'strided_slice'),
+ ('strided_slice', 'reshape_unpack_data'),
+ ('reshape_unpack_data', 'reshape_unpack'),
+ ('reshape_unpack', 'strided_slice_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 16])},
+ 'reshape_split/Permute_before_data': {'shape': np.array([1, 227, 16, 227])},
+ 'reshape_split_data': {'shape': np.array([1, 227, 227, 8, 2])},
+ 'reshape_pack_data': {'shape': np.array([1, 2, 1 * 227 * 227 * 8])},
+ 'reshape_pack/Permute_after_data': {'shape': np.array([1, 227 * 227 * 8, 2])},
+ 'softmax_1_data': {'shape': np.array([1, 2, 1 * 227 * 227 * 8])},
+ 'reshape_unpack_data': {'shape': np.array([1, 1, 227 * 227 * 8])},
+ 'strided_slice_data': {'shape': np.array([1, 227, 227, 8])}
+ })
+
+ pattern = PixelLinkReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'strided_slice_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/Reduce_test.py b/model-optimizer/extensions/middle/Reduce_test.py
new file mode 100644
index 000000000..1925df1a7
--- /dev/null
+++ b/model-optimizer/extensions/middle/Reduce_test.py
@@ -0,0 +1,320 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.middle.Reduce import ReduceReplacer
+from mo.middle.passes.eliminate_test import build_graph
+from mo.middle.passes.fusing.fuse_linear_ops_test import compare_graphs
+
+# The dictionary with nodes attributes used to build various graphs. A key is the name of the node and the value is the
+# dictionary with node attributes.
+nodes_attributes = {
+ # Placeholder layers
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_4_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+
+ # Reshape layers
+ 'reduce_1': {'type': 'Reduce', 'kind': 'op', 'op': 'Reduce'},
+ 'reduce_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ # Reshape layers
+ 'reshape_1': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'reshape_2': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ # Pooling
+ 'pooling': {'type': 'Pooling', 'kind': 'op', 'op': 'Pooling'},
+ 'pooling_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ # Power
+ 'power': {'type': 'Power', 'kind': 'op', 'op': 'Power'},
+ 'power_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ # Concat
+ 'concat': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+}
+
+
+class ReduceReplacerTest(unittest.TestCase):
+ def test1(self):
+ # Original graph
+ # data(1,64,1)-->Reduce(axis=1,keep_dims=True)-->data(1,1,1)
+ #
+ # Reference graph
+ # data(1,61,1)->Reshape(1,1,64,1)->Pool(1,1,1,1)->Reshape(1,1,1)
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reduce_1'),
+ ('reduce_1', 'reduce_1_data'),
+ ('reduce_1_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 64, 1])},
+ 'reduce_1': {'axis': np.array([1]), 'keep_dims': True, 'reduce_type': 'Mean'},
+ 'reduce_1_data': {'shape': np.array([1, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'pooling'),
+ ('pooling', 'pooling_data'),
+ ('pooling_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 64, 1])},
+ 'reshape_1': {'dim': np.array([1, 1, 64, 1])},
+ 'reshape_1_data': {'shape': np.array([1, 1, 64, 1])},
+ 'pooling': {'window': np.array([1, 1, 64, 1])},
+ 'pooling_data': {'shape': np.array([1, 1, 1, 1])},
+ 'reshape_2': {'dim': np.array([1, 1, 1])},
+ 'reshape_2_data': {'shape': np.array([1, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ pattern = ReduceReplacer()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test2(self):
+ # Original graph
+ # data(1,3,64,64)-->Reduce(axis=2,keep_dims=True)-->data(1,3,1,64)
+ #
+ # Reference graph
+ # data(1,3,64,64)->Reshape->Pool(1,3,1,64)->Reshape(1,3,1,64)
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reduce_1'),
+ ('reduce_1', 'reduce_1_data'),
+ ('reduce_1_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'reduce_1': {'axis': np.array([2]), 'keep_dims': True, 'reduce_type': 'Mean'},
+ 'reduce_1_data': {'shape': np.array([1, 3, 1, 64])},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'pooling'),
+ ('pooling', 'pooling_data'),
+ ('pooling_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'reshape_1': {'dim': np.array([1, 3, 64, 64])},
+ 'reshape_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'pooling': {'window': np.array([1, 1, 64, 1])},
+ 'pooling_data': {'shape': np.array([1, 3, 1, 64])},
+ 'reshape_2': {'dim': np.array([1, 3, 1, 64])},
+ 'reshape_2_data': {'shape': np.array([1, 3, 1, 64])},
+ }, nodes_with_edges_only=True)
+
+ pattern = ReduceReplacer()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test3(self):
+ # Original graph
+ # data(1,3,64,64)-->Reduce(axis=[2,3],keep_dims=True)-->data(1,3,1,1)
+ #
+ # Reference graph
+ # data(1,3,64,64)->Reshape->Pool(1,3,1,1)->Reshape(1,3,1,1)
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reduce_1'),
+ ('reduce_1', 'reduce_1_data'),
+ ('reduce_1_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'reduce_1': {'axis': np.array([2, 3]), 'keep_dims': True, 'reduce_type': 'Mean'},
+ 'reduce_1_data': {'shape': np.array([1, 3, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'pooling'),
+ ('pooling', 'pooling_data'),
+ ('pooling_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 64, 64])},
+ 'reshape_1': {'dim': np.array([1, 3, 64 * 64, 1])},
+ 'reshape_1_data': {'shape': np.array([1, 3, 64 * 64, 1])},
+ 'pooling': {'window': np.array([1, 1, 64 * 64, 1])},
+ 'pooling_data': {'shape': np.array([1, 3, 1, 1])},
+ 'reshape_2': {'dim': np.array([1, 3, 1, 1])},
+ 'reshape_2_data': {'shape': np.array([1, 3, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ pattern = ReduceReplacer()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test4(self):
+ # Original graph
+ # data(2,3,64,64)-->Reduce(axis=[1,2,3],keep_dims=False)-->data(2)
+ #
+ # Reference graph
+ # data(2,3,64,64)->Reshape(2,1,3*64*64,1)->Pool(2,1,1,1)->Reshape(2)
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reduce_1'),
+ ('reduce_1', 'reduce_1_data'),
+ ('reduce_1_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([2, 3, 64, 64])},
+ 'reduce_1': {'axis': np.array([1, 2, 3]), 'keep_dims': False, 'reduce_type': 'Mean'},
+ 'reduce_1_data': {'shape': np.array([2])},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'pooling'),
+ ('pooling', 'pooling_data'),
+ ('pooling_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([2, 3, 64, 64])},
+ 'reshape_1': {'dim': np.array([2, 1, 3 * 64 * 64, 1])},
+ 'reshape_1_data': {'shape': np.array([2, 1, 3 * 64 * 64, 1])},
+ 'pooling': {'window': np.array([1, 1, 3 * 64 * 64, 1])},
+ 'pooling_data': {'shape': np.array([2, 1, 1, 1])},
+ 'reshape_2': {'dim': np.array([2])},
+ 'reshape_2_data': {'shape': np.array([2])},
+ }, nodes_with_edges_only=True)
+
+ pattern = ReduceReplacer()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test5(self):
+ # Original graph
+ # data(1, 16, 64, 64, 64, 4)-->Reduce(axis=[5],keep_dims=False)-->data(1, 16, 64, 64, 64)
+ #
+ # Reference graph
+ # data(1, 16, 64, 64, 64, 4)->Reshape(1*16*64*64, 64, 4, 1)->Pool(1, 1, 4, 1)->Reshape(1, 16, 64, 64, 64)
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reduce_1'),
+ ('reduce_1', 'reduce_1_data'),
+ ('reduce_1_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 16, 64, 64, 64, 4])},
+ 'reduce_1': {'axis': np.array([5]), 'keep_dims': False, 'reduce_type': 'max'},
+ 'reduce_1_data': {'shape': np.array([1, 16, 64, 64, 64])},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'pooling'),
+ ('pooling', 'pooling_data'),
+ ('pooling_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 16, 64, 64, 64, 4])},
+ 'reshape_1': {'dim': np.array([65536, 64, 4, 1])},
+ 'reshape_1_data': {'shape': np.array([65536, 64, 4, 1])},
+ 'pooling': {'window': np.array([1, 1, 4, 1])},
+ 'pooling_data': {'shape': np.array([65536, 64, 1, 1])},
+ 'reshape_2': {'dim': np.array([1, 16, 64, 64, 64])},
+ 'reshape_2_data': {'shape': np.array([1, 16, 64, 64, 64])},
+ }, nodes_with_edges_only=True)
+
+ pattern = ReduceReplacer()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test6(self):
+ # Original graph
+ # data(1,64,1)-->Reduce(axis=-2,keep_dims=True, reduce_type=Sum)-->data(1,1,1)
+ #
+ # Reference graph
+ # data(1,61,1)->Reshape(1,1,64,1)->Pool(1,1,1,1)->Reshape(1,1,1)->Power(scale=64)
+ #
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reduce_1'),
+ ('reduce_1', 'reduce_1_data'),
+ ('reduce_1_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 64, 1])},
+ 'reduce_1': {'axis': np.array([-2]), 'keep_dims': True, 'reduce_type': 'Sum'},
+ 'reduce_1_data': {'shape': np.array([1, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'pooling'),
+ ('pooling', 'pooling_data'),
+ ('pooling_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data'),
+ ('reshape_2_data', 'power'),
+ ('power', 'power_data'),
+ ('power_data', 'concat'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 64, 1])},
+ 'reshape_1': {'dim': np.array([1, 1, 64, 1])},
+ 'reshape_1_data': {'shape': np.array([1, 1, 64, 1])},
+ 'pooling': {'window': np.array([1, 1, 64, 1])},
+ 'pooling_data': {'shape': np.array([1, 1, 1, 1])},
+ 'reshape_2': {'dim': np.array([1, 1, 1])},
+ 'reshape_2_data': {'shape': np.array([1, 1, 1])},
+ 'power': {'scale': 64.0},
+ 'power_data': {'shape': np.array([1, 1, 1])},
+ }, nodes_with_edges_only=True)
+
+ pattern = ReduceReplacer()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/ShuffleChannel_test.py b/model-optimizer/extensions/middle/ShuffleChannel_test.py
new file mode 100644
index 000000000..4b1e7e44b
--- /dev/null
+++ b/model-optimizer/extensions/middle/ShuffleChannel_test.py
@@ -0,0 +1,53 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+from extensions.middle.ShuffleChannel import ShuffleChannel
+from mo.utils.unittest.graph import build_graph_with_attrs, compare_graphs
+
+
+class ShuffleChannelTests(unittest.TestCase):
+ def test_1(self):
+ graph = build_graph_with_attrs(
+ nodes_with_attrs=[('data', {'shape': [1, 10, 128, 128], 'kind': 'data'}),
+ ('shuffle', {'type': 'ShuffleChannel', 'kind': 'op', 'op': 'ShuffleChannel', 'group': 2}),
+ ('out_data', {'shape': [1, 10, 128, 128], 'kind': 'data'}),
+ ],
+ edges_with_attrs=[('data', 'shuffle'), ('shuffle', 'out_data')]
+ )
+ graph.graph['layout'] = 'NCHW'
+
+ graph_ref = build_graph_with_attrs(
+ nodes_with_attrs=[('data', {'shape': [1, 10, 128, 128], 'kind': 'data'}),
+ ('split', {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape', 'dim': [1, 2, 5, -1]}),
+ ('split_data', {'shape': [1, 2, 5, 128*128], 'kind': 'data'}),
+ ('transpose', {'type': 'Permute', 'kind': 'op', 'op': 'Permute', 'order': [0, 2, 1, 3]}),
+ ('transpose_data', {'shape': [1, 5, 2, 128*128], 'kind': 'data'}),
+ ('concat', {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape', 'dim': [1, 10, 128, 128]}),
+ ('out_data', {'shape': [1, 10, 128, 128], 'kind': 'data'}),
+ ],
+ edges_with_attrs=[('data', 'split'),
+ ('split', 'split_data'),
+ ('split_data', 'transpose'),
+ ('transpose', 'transpose_data'),
+ ('transpose_data', 'concat'),
+ ('concat', 'out_data')],
+ )
+ pattern = ShuffleChannel()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'out_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/ShufflenetReshape_test.py b/model-optimizer/extensions/middle/ShufflenetReshape_test.py
new file mode 100644
index 000000000..d75c83d9f
--- /dev/null
+++ b/model-optimizer/extensions/middle/ShufflenetReshape_test.py
@@ -0,0 +1,200 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.middle.ShufflenetReshape import FeatureShuffleReshape, ReshapeSoftmaxReshape
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Reshape layers
+ 'reshape_1': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape', 'dim': None},
+ 'reshape_1_data': {'name': 'reshape_1_data', 'value': None, 'shape': None, 'kind': 'data'},
+ 'reshape_2': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_2_data': {'name': 'reshape_2_data', 'value': None, 'shape': None, 'kind': 'data'},
+ 'reshape_3': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_3_data': {'name': 'reshape_3_data', 'value': None, 'shape': None, 'kind': 'data'},
+ # Transpose layer
+ 'transpose_1': {'type': 'Permute', 'kind': 'op', 'op': 'Transpose'},
+ 'transpose_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Softmax layer
+ 'softmax_1': {'type': 'SoftMax', 'kind': 'op', 'op': 'SoftMax'},
+ 'softmax_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class FeatureShuffleReshapeTests(unittest.TestCase):
+ def test_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'transpose_1'),
+ ('transpose_1', 'transpose_1_data'),
+ ('transpose_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'reshape_1_data': {'shape': np.array([227, 227, 4, 28])},
+ 'transpose_1': {'order': np.array([0, 1, 3, 2])},
+ 'transpose_1_data': {'shape': np.array([227, 227, 28, 4])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 112])},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'transpose_1'),
+ ('transpose_1', 'transpose_1_data'),
+ ('transpose_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 112])},
+ 'reshape_1_data': {'shape': np.array([1, 4, 28, 227 * 227])},
+ 'transpose_1': {'order': np.array([0, 2, 1, 3])},
+ 'transpose_1_data': {'shape': np.array([1, 28, 4, 227 * 227])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 112])},
+ 'reshape_3_data': {'shape': np.array([1, 227, 227, 112])},
+ })
+
+ pattern = FeatureShuffleReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'reshape_2_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'transpose_1'),
+ ('transpose_1', 'transpose_1_data'),
+ ('transpose_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 112, 227, 227])},
+ 'reshape_1_data': {'shape': np.array([1, 4, 28, 227, 227])},
+ 'transpose_1': {'order': np.array([0, 2, 1, 3, 4])},
+ 'transpose_1_data': {'shape': np.array([1, 28, 4, 227, 227])},
+ 'reshape_2_data': {'shape': np.array([1, 112, 227, 227])},
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'transpose_1'),
+ ('transpose_1', 'transpose_1_data'),
+ ('transpose_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 112, 227, 227])},
+ 'reshape_1_data': {'shape': np.array([1, 4, 28, 227 * 227])},
+ 'transpose_1': {'order': np.array([0, 2, 1, 3])},
+ 'transpose_1_data': {'shape': np.array([1, 28, 4, 227 * 227])},
+ 'reshape_2_data': {'shape': np.array([1, 112, 227, 227])},
+ })
+
+ pattern = FeatureShuffleReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'reshape_2_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+
+class ReshapeSoftmaxReshapeTests(unittest.TestCase):
+ def test_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'softmax_1'),
+ ('softmax_1', 'softmax_1_data'),
+ ('softmax_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 2])},
+ 'reshape_1': {'dim': np.array([1, 227 * 227, 2])},
+ 'reshape_1_data': {'shape': np.array([1 * 227 * 227, 2])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 2])},
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'softmax_1'),
+ ('softmax_1', 'softmax_1_data'),
+ ('softmax_1_data', 'reshape_3'),
+ ('reshape_3', 'reshape_3_data'),
+ ('reshape_3_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 2])},
+ 'reshape_1_data': {'shape': np.array([1, 2, 227 * 227])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 2])},
+ })
+
+ pattern = ReshapeSoftmaxReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'reshape_2_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'softmax_1'),
+ ('softmax_1', 'softmax_1_data'),
+ ('softmax_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 2])},
+ 'reshape_1_data': {'shape': np.array([1 * 227 * 227, 2])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 2])},
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data'),
+ ('reshape_1_data', 'softmax_1'),
+ ('softmax_1', 'softmax_1_data'),
+ ('softmax_1_data', 'reshape_2'),
+ ('reshape_2', 'reshape_2_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 2])},
+ 'reshape_1_data': {'shape': np.array([1 * 227 * 227, 2])},
+ 'reshape_2_data': {'shape': np.array([1, 227, 227, 2])},
+ })
+
+ pattern = ReshapeSoftmaxReshape()
+ pattern.find_and_replace_pattern(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'reshape_2_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/SliceConvert_test.py b/model-optimizer/extensions/middle/SliceConvert_test.py
new file mode 100644
index 000000000..f282d5e0d
--- /dev/null
+++ b/model-optimizer/extensions/middle/SliceConvert_test.py
@@ -0,0 +1,124 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+import numpy as np
+
+from extensions.middle.SliceConverter import ConvertSlice
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph, compare_graphs
+from mo.ops.slice import Slice
+
+nodes_attributes = {
+ # input data
+ 'placeholder_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Slice layer
+ 'slice': {'type': 'Slice', 'kind': 'op', 'op': 'Slice'},
+ 'slice_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Output operation
+ 'output_op': {'type': 'Const', 'value': None, 'kind': 'op', 'op': 'Const'},
+ 'output_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Crop layer
+ 'crop': {'type': 'Crop', 'kind': 'op', 'op': 'Crop', 'axis': None, 'offset': None, 'dim': None},
+ 'dim': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # StridedSlice layer
+ 'strided_slice': {'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice', 'slices': None,
+ 'shrink_axis_mask': None}
+}
+
+
+class ConvertSliceTests(unittest.TestCase):
+ def test_1(self):
+ """
+ Testing case with non-constant path and multiple
+ slicing dimensions
+ :return:
+ """
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'slice'),
+ ('slice', 'slice_data'),
+ ('slice_data', 'output_op'),
+ ('output_op', 'output_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([4, 5, 6])},
+ 'slice': {'start': np.array([1, 2, 3]), 'end': np.array([3, 4, 4]), 'axis': None},
+ 'output_op': {'is_output': True},
+ }
+ )
+ slice_node = Node(graph, 'slice')
+ Slice.infer(slice_node)
+
+ pattern = ConvertSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'crop'),
+ ('crop', 'slice_data'),
+ ('slice_data', 'output_op'),
+ ('output_op', 'output_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([4, 5, 6])},
+ 'crop': {'axis': np.array([0, 1, 2]), 'offset': np.array([1, 2, 3]),
+ },
+ 'output_op': {'is_output': True},
+ 'dim': {'dim': np.array([2, 2, 1])},
+ }
+ )
+ (flag, resp) = compare_graphs(graph, graph_ref, 'output_op', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_2(self):
+ """
+ Testing case with constant path and one
+ slicing dimension
+ """
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'slice'),
+ ('slice', 'slice_data'),
+ ('slice_data', 'output_op'),
+ ('output_op', 'output_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([4, 5, 6])},
+ 'slice': {'start': np.array([1]), 'end': np.array([3]), 'axis': None},
+ 'output_op': {'is_output': True}
+ }
+ )
+ slice_node = Node(graph, 'slice')
+ Slice.infer(slice_node)
+
+ pattern = ConvertSlice()
+ pattern.find_and_replace_pattern(graph)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'strided_slice'),
+ ('strided_slice', 'slice_data'),
+ ('slice_data', 'output_op'),
+ ('output_op', 'output_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([4, 5, 6])},
+ 'strided_slice': {'slices': np.array([slice(1, 3, 1),slice(0, 5, 1),slice(0, 6, 1)]),
+ 'shrink_axis_mask': np.array([False, False, False])},
+ 'output_op': {'is_output': True}
+ }
+ )
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'output_op', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/TensorIteratorBackEdge_test.py b/model-optimizer/extensions/middle/TensorIteratorBackEdge_test.py
new file mode 100644
index 000000000..c4482c466
--- /dev/null
+++ b/model-optimizer/extensions/middle/TensorIteratorBackEdge_test.py
@@ -0,0 +1,84 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from extensions.middle.TensorIteratorBackEdge import BackEdgesMatching
+from mo.utils.unittest.graph import compare_graphs, build_graph_with_attrs
+
+
+class BackEdgesMatchingTests(unittest.TestCase):
+ def test_no_exit(self):
+ pattern_matcher = BackEdgesMatching()
+ pattern = pattern_matcher.pattern()
+ graph = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'], update_edge_attrs=None,
+ new_nodes_with_attrs=[('from_body_data', {'kind':'data'})],
+ new_edges_with_attrs=[('from_body_data', 'NextIteration')])
+
+ pattern_matcher.find_and_replace_pattern(graph)
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=[('condition', {'kind': 'op', 'op':'TensorIteratorCondition'}),
+ ('condition_data', {'kind': 'data'}),
+ ('back_edge', {'kind': 'op', 'op': 'TensorIteratorBackEdge'}),
+ ('enter_data', {'kind': 'data'}),
+ ('from_body_data', {'kind': 'data'}),
+ ('Identity_1_data', {'kind': 'data'}),],
+ edges_with_attrs=[('condition', 'condition_data'),
+ ('enter_data', 'back_edge', {'in': 0}),
+ ('condition_data', 'back_edge', {'in': 2}), # {in:2}
+ ('from_body_data', 'back_edge', {'in': 1}),
+ ('back_edge', 'Identity_1_data')],
+ update_edge_attrs=None,
+ new_nodes_with_attrs=[],
+ new_edges_with_attrs=[],
+ )
+ (flag, resp) = compare_graphs(graph, graph_ref, 'Identity_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_with_exit(self):
+ pattern_matcher = BackEdgesMatching()
+ pattern = pattern_matcher.pattern()
+ graph = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'], update_edge_attrs=None,
+ new_nodes_with_attrs=[('from_body_data', {'kind':'data'}),
+ ('exit', {'kind': 'op', 'op': 'Exit', 'name': 'exit'}),
+ ('exit_data', {'kind':'data'})],
+ new_edges_with_attrs=[('from_body_data', 'NextIteration'),
+ ('Switch_1', 'exit', {'out': 0}),
+ ('exit', 'exit_data')])
+
+ pattern_matcher.find_and_replace_pattern(graph)
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=[('condition', {'kind': 'op', 'op':'TensorIteratorCondition'}),
+ ('condition_data', {'kind': 'data'}),
+ ('back_edge', {'kind': 'op', 'op': 'TensorIteratorBackEdge'}),
+ ('enter_data', {'kind': 'data'}),
+ ('from_body_data', {'kind': 'data'}),
+ ('Identity_1_data', {'kind': 'data'}),
+ ('output', {'kind':'op', 'op':'TensorIteratorOutput'}),
+ ('exit_data', {'kind': 'data'})
+ ],
+ edges_with_attrs=[('condition', 'condition_data'),
+ ('enter_data', 'back_edge', {'in': 0}),
+ ('condition_data', 'back_edge', {'in': 2}),
+ ('from_body_data', 'back_edge', {'in': 1}),
+ ('back_edge', 'Identity_1_data'),
+ ('condition_data', 'output'),
+ ('output', 'exit_data'),
+ ('from_body_data', 'output')],
+ update_edge_attrs=None,
+ new_nodes_with_attrs=[],
+ new_edges_with_attrs=[],
+ )
+ (flag, resp) = compare_graphs(graph, graph_ref, 'Identity_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp) \ No newline at end of file
diff --git a/model-optimizer/extensions/middle/TensorIteratorCondition_test.py b/model-optimizer/extensions/middle/TensorIteratorCondition_test.py
new file mode 100644
index 000000000..8ebd9dd16
--- /dev/null
+++ b/model-optimizer/extensions/middle/TensorIteratorCondition_test.py
@@ -0,0 +1,70 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+import numpy as np
+
+from extensions.middle.TensorIteratorCondition import LoopConditionMatcher
+from mo.utils.unittest.graph import build_graph_with_attrs, compare_graphs
+
+
+class TensorIteratorConditionTests(unittest.TestCase):
+ def test(self):
+ pattern_matcher = LoopConditionMatcher()
+ pattern = pattern_matcher.pattern()
+
+ graph = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'],
+ new_nodes_with_attrs=[('maximum', {'kind':'op', 'op': 'Maximum'}),
+ ('maximum_data', {'kind': 'data'})],
+ new_edges_with_attrs=[('maximum', 'maximum_data'),
+ ('maximum_data', 'minimum', {'in':1})],
+ update_nodes_attributes=[('init_1_data', {'value': np.array([0])}),
+ ('init_2_data', {'value': np.array([0])}),
+ ('add_1_y_data', {'value': np.array(1)}),
+ ('add_2_y_data', {'value': np.array(1)}),
+ ('loop_cond_data', {'value': None}),
+ ('Identity_2_data', {'value': None}),
+ ],
+ update_edge_attrs={('Strided_slice_data', 'minimum',0): {'in': 0}})
+
+ pattern_matcher.find_and_replace_pattern(graph)
+ graph_ref = build_graph_with_attrs(
+ nodes_with_attrs=[('TensorIteratorCondition', {'kind': 'op', 'op': 'TensorIteratorCondition'}),
+ ('loop_cond_data', {'kind': 'data'}),
+ ('identity_data', {'kind': 'data'}),
+ ('StridedSlice', {'kind': 'op', 'op':'StridedSlice'}),
+ ('StridedSlice_data', {'kind': 'data'}),
+ ('Maximum', {'kind': 'op', 'op': 'Maximum'}),
+ ('Maximum_data', {'kind': 'data'}),
+ ('minimum', {'kind': 'op', 'op': 'Minimum'}),
+ ('minimum_data', {'kind': 'data'}),
+ ],
+ edges_with_attrs=[('Maximum', 'Maximum_data'),
+ ('Maximum_data', 'minimum'),
+ ('StridedSlice', 'StridedSlice_data'),
+ ('StridedSlice_data', 'TensorIteratorCondition', {'in':0}),
+ ('StridedSlice_data', 'minimum'),
+ ('minimum', 'minimum_data'),
+ ('minimum_data', 'TensorIteratorCondition', {'in':1}),
+ ('TensorIteratorCondition', 'loop_cond_data'),
+ ('TensorIteratorCondition', 'identity_data'),
+ ],
+ update_edge_attrs=None,
+ new_nodes_with_attrs=[],
+ new_edges_with_attrs=[],
+ )
+ (flag, resp) = compare_graphs(graph, graph_ref, 'loop_cond_data', check_op_attrs=True)
+ self.assertTrue(flag, resp) \ No newline at end of file
diff --git a/model-optimizer/extensions/middle/TensorIteratorInput_test.py b/model-optimizer/extensions/middle/TensorIteratorInput_test.py
new file mode 100644
index 000000000..efd560c6d
--- /dev/null
+++ b/model-optimizer/extensions/middle/TensorIteratorInput_test.py
@@ -0,0 +1,163 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+import numpy as np
+
+from extensions.middle.TensorIteratorInput import SmartInputMatcher, SimpleInputMatcher, BackEdgeSimpleInputMatcher
+from mo.utils.unittest.graph import build_graph_with_attrs, compare_graphs
+
+
+class SmartInputMatcherTests(unittest.TestCase):
+ def test(self):
+ pattern_matcher = SmartInputMatcher()
+ pattern = pattern_matcher.pattern()
+
+ graph = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'],
+ update_edge_attrs={('range_data', 'TensorArrayScatter', 0): {'in': 1},
+ ('TensorArray_handle', 'TensorArrayScatter', 0): {'in': 0},
+ ('TensorArray_flow', 'TensorArrayScatter', 0): {'in': 3}},
+ new_nodes_with_attrs=[('ta_size', {'kind': 'data'}),
+ ('ta_size_op', {'kind': 'op'}),
+ ('value', {'kind': 'data'}),
+ ],
+ new_edges_with_attrs=[
+ ('ta_size_op', 'ta_size'),
+ ('ta_size', 'TensorArray'),
+ ('value', 'TensorArrayScatter', {'in':2}),
+ ],
+ update_nodes_attributes=[('Enter_data', {'value': np.array([1])}),
+ ('stack_data', {'value': np.array([0])}),
+ ('stack_1_data', {'value': np.array([1])}),
+ ('stack_2_data', {'value': np.array([1])}),
+ ('start_data', {'value': np.array([0])}),
+ ('delta_data', {'value': np.array([1])})
+ ])
+
+ pattern_matcher.find_and_replace_pattern(graph)
+ graph_ref = build_graph_with_attrs(
+ nodes_with_attrs=[('condition_data', {'kind': 'data'}),
+ ('TensorIteratorInput', {'kind': 'op', 'op': 'TensorIteratorInput'}),
+ ('TensorArrayRead_data', {'kind': 'data'}),
+ ('condition_data', {'kind': 'data'}),
+ ('value', {'kind': 'data'}),
+ ('ta_size', {'kind': 'data'}),
+ ('ta_size_op', {'kind': 'op'})],
+ edges_with_attrs=[('ta_size', 'TensorIteratorInput', {'in': 0}),
+ ('condition_data', 'TensorIteratorInput', {'in': 2}),
+ ('value', 'TensorIteratorInput', {'in': 1}),
+ ('TensorIteratorInput', 'TensorArrayRead_data'),
+ ('ta_size_op', 'ta_size')],
+ update_edge_attrs=None,
+ new_nodes_with_attrs=[],
+ new_edges_with_attrs=[],
+ )
+ (flag, resp) = compare_graphs(graph, graph_ref, 'TensorArrayRead_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+
+class SimpleInputMatcherTest(unittest.TestCase):
+ def test(self):
+ pattern_matcher = SimpleInputMatcher()
+ pattern = pattern_matcher.pattern()
+
+ graph = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'],
+ update_edge_attrs=None,
+ new_nodes_with_attrs=[('in_node', {'kind': 'data'}),
+ ('Enter_data', {'kind': 'data'})],
+ new_edges_with_attrs=[('in_node', 'Enter'), ('Enter', 'Enter_data')],
+ update_nodes_attributes=[])
+
+ pattern_matcher.find_and_replace_pattern(graph)
+
+ graph_ref = build_graph_with_attrs(
+ nodes_with_attrs=[('TensorIteratorInput', {'kind': 'op', 'op': 'TensorIteratorInput'}),
+ ('in_node', {'kind': 'data'}),
+ ('Enter_data', {'kind': 'data'})
+ ],
+ edges_with_attrs=[('in_node', 'TensorIteratorInput'), ('TensorIteratorInput', 'Enter_data')],
+ )
+ (flag, resp) = compare_graphs(graph, graph_ref, 'Enter_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+
+class BackEdgeInputMatcherTest(unittest.TestCase):
+ def test1(self):
+ """
+ Case with constant input to init
+ """
+ pattern_matcher = BackEdgeSimpleInputMatcher()
+ pattern = pattern_matcher.pattern()
+
+ graph = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'],
+ new_nodes_with_attrs=[('cycle_data', {'kind': 'data'}),
+ ('condition', {'kind': 'data'}),
+ ('init', {'kind': 'data', 'shape': np.array([1,3])}),
+ ],
+ new_edges_with_attrs=[('condition', 'BackEdge', {'in': 2}),
+ ('init', 'BackEdge', {'in': 0}),
+ ('cycle_data', 'BackEdge', {'in': 1})],)
+
+ pattern_matcher.find_and_replace_pattern(graph)
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'],
+ new_nodes_with_attrs=[('cycle_data', {'kind': 'data'}),
+ ('condition', {'kind': 'data'}),
+ ('init', {'kind': 'data', 'shape': np.array([1,3])}),
+ ('TensorIteratorInput', {'kind': 'op', 'op': 'TensorIteratorInput'}),
+ ('TensorIteratorInput_data', {'kind': 'data', 'shape': np.array([1,3])}),
+ ],
+ new_edges_with_attrs=[('TensorIteratorInput_data', 'TensorIteratorInput'),
+ ('TensorIteratorInput', 'init'),
+ ('condition', 'BackEdge', {'in': 2}),
+ ('init', 'BackEdge', {'in': 0}),
+ ('cycle_data', 'BackEdge', {'in': 1})],)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'BackEdge', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test2(self):
+ """
+ Case with non-constant input to init.
+ Nothing should happen with graph.
+ """
+ pattern_matcher = BackEdgeSimpleInputMatcher()
+ pattern = pattern_matcher.pattern()
+
+ graph = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'],
+ new_nodes_with_attrs=[('cycle_data', {'kind': 'data'}),
+ ('condition', {'kind': 'data'}),
+ ('init', {'kind': 'data', 'shape': np.array([1, 3])}),
+ ('Enter', {'kind': 'op', 'op': 'Enter'}),
+ ],
+ new_edges_with_attrs=[('Enter', 'init'),
+ ('condition', 'BackEdge', {'in': 2}),
+ ('init', 'BackEdge', {'in': 0}),
+ ('cycle_data', 'BackEdge', {'in': 1})])
+
+ pattern_matcher.find_and_replace_pattern(graph)
+
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'],
+ new_nodes_with_attrs=[('cycle_data', {'kind': 'data'}),
+ ('condition', {'kind': 'data'}),
+ ('init', {'kind': 'data', 'shape': np.array([1, 3])}),
+ ('Enter', {'kind': 'op', 'op': 'Enter'}),
+ ],
+ new_edges_with_attrs=[('Enter', 'init'),
+ ('condition', 'BackEdge', {'in': 2}),
+ ('init', 'BackEdge', {'in': 0}),
+ ('cycle_data', 'BackEdge', {'in': 1})], )
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'BackEdge', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/TensorIteratorOutput_test.py b/model-optimizer/extensions/middle/TensorIteratorOutput_test.py
new file mode 100644
index 000000000..d6aa9402f
--- /dev/null
+++ b/model-optimizer/extensions/middle/TensorIteratorOutput_test.py
@@ -0,0 +1,63 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+import numpy as np
+
+from extensions.middle.TensorIteratorOutput import SmartOutputMatcher
+from mo.utils.unittest.graph import build_graph_with_attrs, compare_graphs
+
+
+class SmartOutputMatcherTests(unittest.TestCase):
+ def test(self):
+ pattern_matcher = SmartOutputMatcher()
+ pattern = pattern_matcher.pattern()
+
+ graph = build_graph_with_attrs(nodes_with_attrs=pattern['nodes'], edges_with_attrs=pattern['edges'],
+ # update_edge_attrs=None,
+ new_nodes_with_attrs=[('index', {'kind': 'data'}),
+ ('value', {'kind': 'data'}),
+ ('ta_size', {'kind': 'data'}),
+ ],
+ new_edges_with_attrs=[('index', 'TensorArrayWrite', {'in':1}),
+ ('value', 'TensorArrayWrite', {'in': 2}),
+ ('ta_size', 'TensorArray')
+ ],
+ update_nodes_attributes=[('WriteEnter_data', {'value': np.array([1, 1])}),
+
+ ('start_data', {'value': np.array([0])}),
+ ('delta_data', {'value': np.array([1])}),
+ ])
+
+ pattern_matcher.find_and_replace_pattern(graph)
+
+ graph_ref = build_graph_with_attrs(
+ nodes_with_attrs=[
+ ('TensorIteratorOutput', {'kind': 'op', 'op': 'TensorIteratorOutput'}),
+ ('TensorArrayGather_data', {'kind': 'data'}),
+ ('index', {'kind': 'data'}),
+ ('value', {'kind': 'data'}),
+ ('ta_size', {'kind': 'data'}), ],
+ edges_with_attrs=[('ta_size', 'TensorIteratorOutput', {'in': 0}),
+ ('index', 'TensorIteratorOutput', {'in': 2}),
+ ('value', 'TensorIteratorOutput', {'in': 1}),
+ ('TensorIteratorOutput', 'TensorArrayGather_data')],
+ update_edge_attrs=None,
+ new_nodes_with_attrs=[],
+ new_edges_with_attrs=[],
+ )
+ (flag, resp) = compare_graphs(graph, graph_ref, 'TensorArrayGather_data', check_op_attrs=True)
+ self.assertTrue(flag, resp) \ No newline at end of file
diff --git a/model-optimizer/extensions/middle/UselessSridedSlice_test.py b/model-optimizer/extensions/middle/UselessSridedSlice_test.py
new file mode 100644
index 000000000..8fbf2408c
--- /dev/null
+++ b/model-optimizer/extensions/middle/UselessSridedSlice_test.py
@@ -0,0 +1,99 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+import numpy as np
+
+from extensions.middle.UselessStridedSlice import UselessStridedSliceEraser
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ # input data
+ 'placeholder': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_data': {'value': None, 'shape': np.array([4, 5, 6]), 'kind': 'data', 'data_type': None},
+ #
+ 'strided_slice': {'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice', 'shrink_axis_mask': None,
+ 'slices': [slice(0, 4, 1), slice(0, 5, 1), slice(0, 6, 1)]},
+ 'strided_slice_data': {'value': None, 'shape': np.array([4, 5, 6]), 'kind': 'data'},
+ 'strided_slice_input_1_data': {'value': None, 'shape': np.array([3]), 'kind': 'data'},
+ 'strided_slice_input_2_data': {'value': None, 'shape': np.array([3]), 'kind': 'data'},
+ 'strided_slice_input_3_data': {'value': None, 'shape': np.array([3]), 'kind': 'data'},
+ #
+ 'strided_slice_2': {'type': 'StridedSlice', 'kind': 'op', 'op': 'StridedSlice', 'shrink_axis_mask': None,
+ 'slices': [slice(0, 4, 1), slice(0, 5, 1), slice(0, 6, 1)]},
+ 'strided_slice_2_data': {'value': None, 'shape': np.array([4, 5, 6]), 'kind': 'data'},
+ # Output operation
+ 'output_op': {'type': 'OpOutput', 'kind': 'op', 'op': 'OpOutput', 'output_op': {'is_output': True}},
+}
+
+
+class UselessStridedSliceTests(unittest.TestCase):
+ def test_single_stride_slice_removal(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder', 'placeholder_data'),
+ ('placeholder_data', 'strided_slice'),
+ ('strided_slice_input_1_data', 'strided_slice'),
+ ('strided_slice_input_2_data', 'strided_slice'),
+ ('strided_slice_input_3_data', 'strided_slice'),
+ ('strided_slice', 'strided_slice_data'),
+ ('strided_slice_data', 'output_op'),
+ ],
+ {},
+ nodes_with_edges_only=True
+ )
+
+ pattern = UselessStridedSliceEraser()
+ pattern.find_and_replace_pattern(graph)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder', 'placeholder_data'),
+ ('placeholder_data', 'output_op'),
+ ],
+ {'placeholder_data': {'shape': np.array([4, 5, 6])}}
+ )
+ (flag, resp) = compare_graphs(graph, graph_ref, 'output_op', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_consecutive_stride_slices_removal(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder', 'placeholder_data'),
+ ('placeholder_data', 'strided_slice'),
+ ('strided_slice_input_1_data', 'strided_slice'),
+ ('strided_slice_input_2_data', 'strided_slice'),
+ ('strided_slice_input_3_data', 'strided_slice'),
+ ('strided_slice', 'strided_slice_data'),
+ ('strided_slice_data', 'strided_slice_2'),
+ ('strided_slice_input_1_data', 'strided_slice_2'),
+ ('strided_slice_input_2_data', 'strided_slice_2'),
+ ('strided_slice_input_3_data', 'strided_slice_2'),
+ ('strided_slice_2', 'strided_slice_2_data'),
+ ('strided_slice_2_data', 'output_op'),
+ ],
+ {},
+ nodes_with_edges_only=True
+ )
+
+ pattern = UselessStridedSliceEraser()
+ pattern.find_and_replace_pattern(graph)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder', 'placeholder_data'),
+ ('placeholder_data', 'output_op'),
+ ],
+ {'placeholder_data': {'shape': np.array([4, 5, 6])}}
+ )
+ (flag, resp) = compare_graphs(graph, graph_ref, 'output_op', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/middle/lstm_sequence_normalize_test.py b/model-optimizer/extensions/middle/lstm_sequence_normalize_test.py
new file mode 100644
index 000000000..d15e6808e
--- /dev/null
+++ b/model-optimizer/extensions/middle/lstm_sequence_normalize_test.py
@@ -0,0 +1,55 @@
+
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+import numpy as np
+
+from extensions.middle.lstm_sequence_normalize import LSTMSequenceNormalize
+from mo.utils.unittest.graph import compare_graphs, build_graph_with_attrs
+from mo.graph.graph import Node
+
+
+class LSTMSequenceNormalizeTest(unittest.TestCase):
+
+ def test_squeeze_num_directions(self):
+ tested_obj = LSTMSequenceNormalize()
+ pattern = tested_obj.pattern()
+ orig_shape = np.array([10, 1, 20, 128], dtype=np.int64) # seq_length, num_dims, batch_size, data_size
+ new_shape = np.array([10, 20, 128], dtype=np.int64)
+ graph = build_graph_with_attrs(
+ nodes_with_attrs=pattern['nodes'],
+ edges_with_attrs=pattern['edges'],
+ update_edge_attrs={
+ ('W', 'lstm', 0): {'in': 1},
+ ('R', 'lstm', 0): {'in': 2},
+ },
+ new_nodes_with_attrs=[
+ ('output', {'shape': orig_shape}),
+ ],
+ new_edges_with_attrs=[
+ ('lstm', 'output', {'out': 0}),
+ ],
+ )
+
+ lstm = Node(graph, 'lstm')
+ match = {'lstm': lstm}
+ tested_obj.squeeze_num_directions(graph, match)
+ self.assertTrue(np.array_equal(lstm.out_node(0).shape, new_shape))
+ reshape_node = lstm.out_node(0).out_node(0)
+ self.assertTrue(reshape_node.op == 'Reshape')
+ self.assertTrue(np.array_equal(reshape_node.dim, orig_shape))
+ self.assertTrue(reshape_node.out_node(0).id == 'output')
diff --git a/model-optimizer/extensions/ops/accum_test.py b/model-optimizer/extensions/ops/accum_test.py
new file mode 100644
index 000000000..b2762f316
--- /dev/null
+++ b/model-optimizer/extensions/ops/accum_test.py
@@ -0,0 +1,120 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.accum import AccumOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+wrong_attrs_graph = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'accum': {'type': 'Accum', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}}
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ 'accum': {'type': 'Accum', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}}
+
+
+class TestAccumOp(unittest.TestCase):
+ def test_accum_infer_assertion(self):
+ graph = build_graph(wrong_attrs_graph,
+ [('node_1', 'accum'),
+ ('accum', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'accum': {
+ 'top_height': 0,
+ 'top_width': 0,
+ 'size_divisible_by': 0,
+ 'have_reference': 1
+ }
+ })
+
+ accum_node = Node(graph, 'accum')
+ self.assertRaises(AssertionError, AccumOp.accum_infer, accum_node)
+
+ def test_accum_infer_have_reference(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'accum'),
+ ('node_2', 'accum'),
+ ('accum', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 227, 227])},
+ 'accum': {
+ 'top_height': 0,
+ 'top_width': 0,
+ 'size_divisible_by': 0,
+ 'have_reference': 1
+ }
+ })
+
+ accum_node = Node(graph, 'accum')
+ AccumOp.accum_infer(accum_node)
+ exp_shape = np.array([1, 6, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_accum_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'accum'),
+ ('node_2', 'accum'),
+ ('accum', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 227, 227])},
+ 'accum': {
+ 'top_height': 0,
+ 'top_width': 0,
+ 'size_divisible_by': 0,
+ 'have_reference': 0
+ }
+ })
+
+ accum_node = Node(graph, 'accum')
+ AccumOp.accum_infer(accum_node)
+ exp_shape = np.array([1, 6, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_accum_infer_top_height_top_width(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'accum'),
+ ('node_2', 'accum'),
+ ('accum', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 227, 227])},
+ 'accum': {
+ 'top_height': 229,
+ 'top_width': 229,
+ 'size_divisible_by': 0,
+ 'have_reference': 0
+ }
+ })
+
+ accum_node = Node(graph, 'accum')
+ AccumOp.accum_infer(accum_node)
+ exp_shape = np.array([1, 6, 229, 229])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/argmax_test.py b/model-optimizer/extensions/ops/argmax_test.py
new file mode 100644
index 000000000..14edf5e5c
--- /dev/null
+++ b/model-optimizer/extensions/ops/argmax_test.py
@@ -0,0 +1,145 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.argmax import ArgMaxOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'argmax': {'type': 'ArgMax', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestArgMaxOp(unittest.TestCase):
+ def test_caffe_argmax_axis(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'argmax'),
+ ('argmax', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 1025, 2049])},
+ 'argmax': {
+ 'out_max_val': True,
+ 'top_k': 100,
+ 'axis': 2
+ }
+ })
+
+ argmax_node = Node(graph, 'argmax')
+ ArgMaxOp.argmax_infer(argmax_node)
+ exp_shape = np.array([1, 3, 100, 2049])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_argmax_axis_negative(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'argmax'),
+ ('argmax', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 1025, 2049])},
+ 'argmax': {
+ 'out_max_val': True,
+ 'top_k': 100,
+ 'axis': -1
+ }
+ })
+
+ argmax_node = Node(graph, 'argmax')
+ ArgMaxOp.argmax_infer(argmax_node)
+ exp_shape = np.array([1, 3, 1025, 100])
+ res_shape = graph.node['node_3']['shape']
+ self.assertEqual(argmax_node.axis, 3)
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_argmax_no_axis(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'argmax'),
+ ('argmax', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 1025, 2049])},
+ 'argmax': {
+ 'out_max_val': True,
+ 'top_k': 100
+ }
+ })
+
+ argmax_node = Node(graph, 'argmax')
+ ArgMaxOp.argmax_infer(argmax_node)
+ exp_shape = np.array([1, 2, 100, 1])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_argmax_extend_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'argmax'),
+ ('argmax', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3])},
+ 'argmax': {
+ 'out_max_val': True,
+ 'top_k': 100
+ }
+ })
+
+ argmax_node = Node(graph, 'argmax')
+ ArgMaxOp.argmax_infer(argmax_node)
+ exp_shape = np.array([1, 2, 100])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_argmax_out_max_val_false(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'argmax'),
+ ('argmax', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3])},
+ 'argmax': {
+ 'out_max_val': False,
+ 'top_k': 100
+ }
+ })
+
+ argmax_node = Node(graph, 'argmax')
+ ArgMaxOp.argmax_infer(argmax_node)
+ exp_shape = np.array([1, 1, 100])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_argmax_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'argmax'),
+ ('argmax', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': None},
+ 'argmax': {
+ 'out_max_val': False,
+ 'top_k': 100
+ }
+ })
+
+ argmax_node = Node(graph, 'argmax')
+ ArgMaxOp.argmax_infer(argmax_node)
+ res_shape = graph.node['node_3']['shape']
+ self.assertIsNone(res_shape)
diff --git a/model-optimizer/extensions/ops/assert_test.py b/model-optimizer/extensions/ops/assert_test.py
new file mode 100644
index 000000000..37417d5d6
--- /dev/null
+++ b/model-optimizer/extensions/ops/assert_test.py
@@ -0,0 +1,53 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+from unittest.mock import Mock
+
+from extensions.ops.assert_op import Assert
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph_with_edge_attrs
+
+
+class TestAssert(unittest.TestCase):
+ def test_assert_cf_true(self):
+ me_mock = Mock()
+ nodes = {
+ 'input_data': {'kind': 'data', 'executable': True},
+ 'assert': {'type': 'Assert', 'value': None, 'kind': 'op', 'op': 'Assert'},
+ 'assert_data': {'value': True, 'kind': 'data', 'executable': True}}
+ edges = [
+ ('input_data', 'assert', {'in': 0}),
+ ('assert', 'assert_data', {'out': 0, 'control_flow_edge': False})]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ tested_class = Assert(graph=graph, attrs={})
+ node = Node(graph, 'assert')
+ tested_class.assert_control_flow_infer(node=node, is_executable=True, mark_executability=me_mock)
+ me_mock.assert_called_once_with('assert_data', True)
+
+ def test_assert_cf_false(self):
+ me_mock = Mock()
+ nodes = {
+ 'input_data': {'name': 'input', 'kind': 'data', 'executable': True},
+ 'assert': {'name': 'assert', 'type': 'Assert', 'value': None, 'kind': 'op', 'op': 'Assert'},
+ 'assert_data': {'name': 'output', 'value': False, 'kind': 'data', 'executable': True}}
+ edges = [
+ ('input_data', 'assert', {'in': 0}),
+ ('assert', 'assert_data', {'out': 0, 'control_flow_edge': False})]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ tested_class = Assert(graph=graph, attrs={})
+ node = Node(graph, 'assert')
+ tested_class.assert_control_flow_infer(node=node, is_executable=True, mark_executability=me_mock)
+ me_mock.assert_called_once_with('assert_data', False)
diff --git a/model-optimizer/extensions/ops/correlation_test.py b/model-optimizer/extensions/ops/correlation_test.py
new file mode 100644
index 000000000..a47aec206
--- /dev/null
+++ b/model-optimizer/extensions/ops/correlation_test.py
@@ -0,0 +1,58 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.correlation import CorrelationOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ 'corr': {'type': 'Correlation', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestConcatPartialInfer(unittest.TestCase):
+ def test_tf_concat_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'corr'),
+ ('node_2', 'corr'),
+ ('corr', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 227, 227])},
+ 'corr': {'pad': 20,
+ 'kernel_size': 1,
+ 'max_displacement': 20,
+ 'stride_1': 1,
+ 'stride_2': 2,
+ 'single_direction': 0,
+ 'do_abs': False,
+ 'correlation_type': 0}
+ })
+
+ corr_node = Node(graph, 'corr')
+ CorrelationOp.corr_infer(corr_node)
+ exp_shape = np.array([1, 441, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/ctc_greedy_decoder_test.py b/model-optimizer/extensions/ops/ctc_greedy_decoder_test.py
new file mode 100644
index 000000000..b5a921782
--- /dev/null
+++ b/model-optimizer/extensions/ops/ctc_greedy_decoder_test.py
@@ -0,0 +1,51 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.ctc_greedy_decoder import CTCGreedyDecoderOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ 'ctc': {'type': 'CTCGreedyDecoder', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestConcatPartialInfer(unittest.TestCase):
+ def test_tf_concat_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'ctc'),
+ ('node_2', 'ctc'),
+ ('ctc', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([88, 2, 71])},
+ 'node_2': {'shape': np.array([88, 2])},
+ 'ctc': {'ctc_merge_repeated': 1}
+ })
+
+ ctc_node = Node(graph, 'ctc')
+ CTCGreedyDecoderOp.ctc_greedy_decoder_infer(ctc_node)
+ exp_shape = np.array([2, 88, 1, 1])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/data_augmentation_test.py b/model-optimizer/extensions/ops/data_augmentation_test.py
new file mode 100644
index 000000000..d8b30e3e3
--- /dev/null
+++ b/model-optimizer/extensions/ops/data_augmentation_test.py
@@ -0,0 +1,62 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.data_augmentation import DataAugmentationOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {
+ 'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'da': {'type': 'DataAugmentation', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+}
+
+
+class TestConcatPartialInfer(unittest.TestCase):
+ def test_tf_concat_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'da'),
+ ('da', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'da': {'crop_width': 225,
+ 'crop_height': 225,
+ 'write_augmented': "",
+ 'max_multiplier': 255.0,
+ 'augment_during_test': True,
+ 'recompute_mean': 0,
+ 'write_mean': "",
+ 'mean_per_pixel': False,
+ 'mean': 0,
+ 'mode': "add",
+ 'bottomwidth': 0,
+ 'bottomheight': 0,
+ 'num': 0,
+ 'chromatic_eigvec': [0.0]}
+ })
+
+ da_node = Node(graph, 'da')
+ DataAugmentationOp.data_augmentation_infer(da_node)
+ exp_shape = np.array([1, 3, 225, 225])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/depth_to_space_test.py b/model-optimizer/extensions/ops/depth_to_space_test.py
new file mode 100644
index 000000000..26b3c4ec3
--- /dev/null
+++ b/model-optimizer/extensions/ops/depth_to_space_test.py
@@ -0,0 +1,57 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.depth_to_space import DepthToSpaceOp
+from mo.graph.graph import Node
+from mo.utils.error import Error
+from mo.utils.unittest.graph import build_graph
+
+nodes = {
+ 'in_data_node': {'value': None, 'kind': 'data', 'shape': np.array([1, 1024, 576, 256])},
+ 'DtS': {'op': 'DepthToSpace', 'kind': 'op', 'block_size': 2},
+ 'out_data_node': {'value': None, 'kind': 'data', 'shape': None}
+}
+
+edges = [
+ ('in_data_node', 'DtS'),
+ ('DtS', 'out_data_node')
+]
+
+
+class TestDepthToSpacePartialInfer(unittest.TestCase):
+ def test_tf_depth_to_space_infer(self):
+ graph = build_graph(nodes, edges)
+ dts_node = Node(graph, 'DtS')
+ DepthToSpaceOp.depth_to_space_infer(dts_node)
+ exp_shape = np.array([1, 2048, 1152, 64])
+ res_shape = graph.node['out_data_node']['shape']
+ self.assertTrue(np.array_equal(exp_shape, res_shape))
+
+ def test_tf_depth_to_space_infer_error(self):
+ graph = build_graph(nodes, edges)
+ graph.node['in_data_node']['shape'] = np.array([1024, 576, 256])
+ dts_node = Node(graph, 'DtS')
+ self.assertRaises(Error, DepthToSpaceOp.depth_to_space_infer(dts_node))
+
+ def test_tf_depth_to_space_infer_error_1(self):
+ graph = build_graph(nodes, edges)
+ graph.node['in_data_node']['shape'] = np.array([1, 1024, 576, 255])
+ dts_node = Node(graph, 'DtS')
+ self.assertRaises(Error, DepthToSpaceOp.depth_to_space_infer(dts_node))
diff --git a/model-optimizer/extensions/ops/gather_test.py b/model-optimizer/extensions/ops/gather_test.py
new file mode 100644
index 000000000..4f749f78d
--- /dev/null
+++ b/model-optimizer/extensions/ops/gather_test.py
@@ -0,0 +1,59 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.gather import Gather
+from mo.front.common.partial_infer.utils import int64_array
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+
+class TestGatherPartialInfer(unittest.TestCase):
+ @staticmethod
+ def _create_graph():
+ nodes_attributes = {'gather_input': {'shape': None, 'value': None, 'kind': 'data'},
+ 'gather_input2': {'shape': None, 'value': None, 'kind': 'data'},
+ 'gather_node': {'op': 'Gather', 'kind': 'op'},
+ 'gather_output': {'shape': None, 'value': None, 'kind': 'data'}
+ }
+ return build_graph(nodes_attributes,
+ [
+ ('gather_input', 'gather_node'), ('gather_node', 'gather_output'), ('gather_input2', 'gather_node')
+ ],
+ {
+ 'gather_input': {'shape': int64_array([10, 15]), 'value': np.ones((3, 15))},
+ 'gather_input2': {'shape': int64_array([2]), 'value': np.array([0, 2])},
+ 'gather_node': {'axis': 0},
+ })
+
+ def test_gather_infer(self):
+ graph = self._create_graph()
+
+ gather_node = Node(graph, 'gather_node')
+ Gather.infer(gather_node)
+
+ exp_shape = int64_array([2, 15])
+ res_shape = graph.node['gather_output']['shape']
+ res_value = graph.node['gather_output']['value']
+
+ self.assertTrue(np.array_equal(exp_shape, res_shape),
+ 'shapes do not match expected: {} and given: {}'.format(exp_shape, res_shape))
+
+ self.assertTrue(np.array_equal(res_value, np.ones(exp_shape)),
+ 'shapes do not match expected: {} and given: {}'.format(exp_shape, res_shape))
diff --git a/model-optimizer/extensions/ops/grn_test.py b/model-optimizer/extensions/ops/grn_test.py
new file mode 100644
index 000000000..351023fef
--- /dev/null
+++ b/model-optimizer/extensions/ops/grn_test.py
@@ -0,0 +1,45 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'grn': {'type': 'GRN', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}}
+
+
+class TestGRNOp(unittest.TestCase):
+ def test_grn_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'grn'),
+ ('grn', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'grn': {'bias': 1}
+ })
+
+ grn_node = Node(graph, 'grn')
+ copy_shape_infer(grn_node)
+ exp_shape = np.array([1, 3, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/instance_normalization_test.py b/model-optimizer/extensions/ops/instance_normalization_test.py
new file mode 100644
index 000000000..e106f47cb
--- /dev/null
+++ b/model-optimizer/extensions/ops/instance_normalization_test.py
@@ -0,0 +1,28 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import networkx as nx
+
+from extensions.ops.instance_normalization import InstanceNormalization
+
+
+class InstanceNormalizationOp(unittest.TestCase):
+ def test_constructor_supported_attrs(self):
+ graph = nx.MultiDiGraph()
+ op = InstanceNormalization(graph, attrs={'epsilon': 0.1})
+ self.assertEqual(op.supported_attrs(), ['epsilon'])
diff --git a/model-optimizer/extensions/ops/interp_test.py b/model-optimizer/extensions/ops/interp_test.py
new file mode 100644
index 000000000..cf2bbc928
--- /dev/null
+++ b/model-optimizer/extensions/ops/interp_test.py
@@ -0,0 +1,216 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.interp import InterpOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'data'},
+ 'interp': {'type': 'Interp', 'kind': 'op', 'factor': None, 'parse_2nd_input': 'value'},
+ 'node_3': {'type': 'Identity', 'shape': None, 'value': None, 'kind': 'data'}
+ }
+
+
+class TestInterpOp(unittest.TestCase):
+ def test_caffe_interp_infer_shrink(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'interp'),
+ ('interp', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 1025, 2049])},
+ 'interp': {'shrink_factor': 2,
+ 'height': 0,
+ 'width': 0,
+ 'zoom_factor': 1,
+ 'pad_beg': 0,
+ 'pad_end': 0}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ interp_node = Node(graph, 'interp')
+ InterpOp.interp_infer(interp_node)
+ exp_shape = np.array([1, 3, 513, 1025])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_interp_infer_wh(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'interp'),
+ ('interp', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 1024, 1, 1])},
+ 'interp': {'width': 65,
+ 'height': 33,
+ 'zoom_factor': 1,
+ 'shrink_factor': 1,
+ 'pad_beg': 0,
+ 'pad_end': 0}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ interp_node = Node(graph, 'interp')
+ InterpOp.interp_infer(interp_node)
+ exp_shape = np.array([1, 1024, 33, 65])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_interp_infer_zoom(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'interp'),
+ ('interp', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 256, 33, 65])},
+ 'interp': {'zoom_factor': 2,
+ 'height': 0,
+ 'width': 0,
+ 'shrink_factor': 1,
+ 'pad_beg': 0,
+ 'pad_end': 0}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ interp_node = Node(graph, 'interp')
+ InterpOp.interp_infer(interp_node)
+ exp_shape = np.array([1, 256, 66, 130])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_interp_infer_zoom_shrink(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'interp'),
+ ('interp', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 256, 33, 65])},
+ 'interp': {'zoom_factor': 2,
+ 'height': 0,
+ 'width': 0,
+ 'shrink_factor': 2,
+ 'pad_beg': 0,
+ 'pad_end': 0}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ interp_node = Node(graph, 'interp')
+ InterpOp.interp_infer(interp_node)
+ exp_shape = np.array([1, 256, 33, 65])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_interp_infer_zoom_shrink_error(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'interp'),
+ ('interp', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 256, 33, 65])},
+ 'interp': {'zoom_factor': 0,
+ 'height': 0,
+ 'width': 0,
+ 'shrink_factor': 0,
+ 'pad_beg': 0,
+ 'pad_end': 0}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ interp_node = Node(graph, 'interp')
+ InterpOp.interp_infer(interp_node)
+ self.assertIsNone(graph.node['node_3']['shape'])
+
+ def test_caffe_interp_infer_zoom_default(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'interp'),
+ ('interp', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 256, 33, 65])},
+ 'interp': {'zoom_factor': 1,
+ 'height': 0,
+ 'width': 0,
+ 'shrink_factor': 1,
+ 'pad_beg': 0,
+ 'pad_end': 0
+ }
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ interp_node = Node(graph, 'interp')
+ InterpOp.interp_infer(interp_node)
+ exp_shape = np.array([1, 256, 33, 65])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_interp_2_blobs(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'interp'),
+ ('node_2', 'interp'),
+ ('interp', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 256, 33, 66])},
+ 'node_2': {'shape': np.array([1, 1, 3, 6])},
+ 'interp': {'zoom_factor': 1,
+ 'shrink_factor': 1,
+ 'pad_beg': 0,
+ 'pad_end': 0,
+ 'parse_2nd_input': 'shape',
+ }
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ interp_node = Node(graph, 'interp')
+ InterpOp.interp_infer(interp_node)
+ exp_shape = np.array([1, 256, 3, 6])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_tf_interp_infer_two_inputs(self):
+
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'interp'),
+ ('node_2', 'interp'),
+ ('interp', 'node_3')],
+ {'node_1': {'shape': np.array([1, 20, 30, 100])},
+ 'node_2': {'shape': np.array([2]), 'value': np.array([2, 3])}})
+ graph.graph['layout'] = 'NHWC'
+ interp_node = Node(graph, 'interp')
+ InterpOp.interp_infer(interp_node)
+ exp_shape = np.array([1, 2, 3, 100])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_tf_interp_infer_one_input_hw(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'interp'),
+ ('interp', 'node_3')],
+ {'node_1': {'shape': np.array([1, 20, 30, 100])},
+ 'interp': {'height': 4, 'width': 6, 'pad_beg': 0, 'pad_end': 0, 'zoom_factor': None,
+ 'shrink_factor': None}})
+ graph.graph['layout'] = 'NHWC'
+ interp_node = Node(graph, 'interp')
+ InterpOp.interp_infer(interp_node)
+ exp_shape = np.array([1, 4, 6, 100])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/merge_test.py b/model-optimizer/extensions/ops/merge_test.py
new file mode 100644
index 000000000..755da1ab8
--- /dev/null
+++ b/model-optimizer/extensions/ops/merge_test.py
@@ -0,0 +1,99 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+import numpy as np
+
+from extensions.ops.merge import Merge
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph_with_attrs, compare_graphs
+
+
+class TestMerge(unittest.TestCase):
+ nodes = [
+ ('first', {'value': np.ones((2, 2)), 'kind': 'data', 'executable': True, 'shape': np.array([2, 2]),
+ 'is_partial_inferred': True}),
+ ('second', {'value': np.zeros((2, 2)), 'kind': 'data', 'executable': False, 'shape': np.array([2, 2]),
+ 'is_partial_inferred': True}),
+ ('merge', {'type': 'Merge', 'kind': 'op', 'op': 'Merge'}),
+ ('merge_output', {'value': None, 'kind': 'data', 'executable': True, 'shape': None}),
+ ]
+ edges = [
+ ('first', 'merge', {'in': 0}),
+ ('second', 'merge', {'in': 1}),
+ ('merge', 'merge_output', {'out': 0}),
+ ]
+
+ def test_merge_infer_simple_case_one_executable(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges)
+
+ # We should propagate value of the first input since only this input is executable
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ update_nodes_attributes=[('merge_output', {'shape': np.array([2, 2]),
+ 'value': np.ones((2,2))}),
+ ('merge', {'is_not_fully_inferred': False})])
+
+ tested_class = Merge(graph=graph, attrs={})
+ node = Node(graph, 'merge')
+ tested_class.merge_infer(node)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'merge_output', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_merge_infer_complex_case(self):
+ """
+ Case as in cycles when in first visit only one input are inferred and in the second -- both.
+ """
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges,
+ update_nodes_attributes=[('first', {'is_partial_inferred': False,
+ 'value': None}),
+ ('second', {'executable': True})])
+
+ # In first visit we should propagate only shapes
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ update_nodes_attributes=[('second', {'executable': True}),
+ ('first', {'is_partial_inferred': False,
+ 'value': None}),
+ ('merge_output', {'shape': np.array([2, 2]),
+ 'value': None}),
+ ('merge', {'is_not_fully_inferred': True})])
+ tested_class = Merge(graph=graph, attrs={})
+ node = Node(graph, 'merge')
+ tested_class.merge_infer(node)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'merge_output', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Imitate that inputs nodes now is inferred
+ graph.node['first']['is_partial_inferred'] = True
+
+ # Run infer second time
+ tested_class = Merge(graph=graph, attrs={})
+ node = Node(graph, 'merge')
+ tested_class.merge_infer(node)
+
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ update_nodes_attributes=[('second', {'executable': True}),
+ ('first', {'is_partial_inferred': True,
+ 'value': None}),
+ ('merge_output', {'shape': np.array([2, 2]),
+ 'value': None}),
+ ('merge', {'is_not_fully_inferred': False})])
+ (flag, resp) = compare_graphs(graph, graph_ref, 'merge_output', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/extensions/ops/normalize_test.py b/model-optimizer/extensions/ops/normalize_test.py
new file mode 100644
index 000000000..8a15fd6db
--- /dev/null
+++ b/model-optimizer/extensions/ops/normalize_test.py
@@ -0,0 +1,46 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'norm': {'type': 'Normalize', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestNormalize(unittest.TestCase):
+ def test_region_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'norm'),
+ ('norm', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'norm': {}
+ })
+
+ norm_node = Node(graph, 'norm')
+ copy_shape_infer(norm_node)
+ exp_shape = np.array([1, 3, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/priorbox_clustered_test.py b/model-optimizer/extensions/ops/priorbox_clustered_test.py
new file mode 100644
index 000000000..849ba7e02
--- /dev/null
+++ b/model-optimizer/extensions/ops/priorbox_clustered_test.py
@@ -0,0 +1,77 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.priorbox_clustered import PriorBoxClusteredOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'value': None, 'kind': 'data'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'data'},
+ 'pbc': {'type': 'PriorBoxClustered', 'value': None, 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'data'}
+ }
+
+
+class TestPriorBoxClusteredPartialInfer(unittest.TestCase):
+ def test_caffe_priorboxclustered_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'pbc'),
+ ('node_2', 'pbc'),
+ ('pbc', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 384, 19, 19])},
+ 'node_2': {'shape': np.array([1, 3, 300, 300])},
+ 'pbc': {'flip': 0, 'clip': 0, 'variance': [0.1, 0.1, 0.2, 0.2],
+ 'step': 0, 'offset': 0.5, 'width': [1., 1., 1., 1., 1., 1., 1., 1., 1.],
+ 'height': [2., 2., 2., 2., 2., 2., 2., 2., 2.]}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ pbc_node = Node(graph, 'pbc')
+ PriorBoxClusteredOp.priorbox_clustered_infer(pbc_node)
+ exp_shape = np.array([1, 2, 12996])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_tf_priorboxclustered_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'pbc'),
+ ('node_2', 'pbc'),
+ ('pbc', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 19, 19, 384])},
+ 'node_2': {'shape': np.array([1, 300, 300, 3])},
+ 'pbc': {'flip': 0, 'clip': 0, 'variance': [0.1, 0.1, 0.2, 0.2],
+ 'step': 0, 'offset': 0.5, 'width': [1., 1., 1., 1., 1., 1., 1., 1., 1.],
+ 'height': [2., 2., 2., 2., 2., 2., 2., 2., 2.]}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ pbc_node = Node(graph, 'pbc')
+ PriorBoxClusteredOp.priorbox_clustered_infer(pbc_node)
+ exp_shape = np.array([1, 2, 12996])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/priorbox_test.py b/model-optimizer/extensions/ops/priorbox_test.py
new file mode 100644
index 000000000..fbb42a48b
--- /dev/null
+++ b/model-optimizer/extensions/ops/priorbox_test.py
@@ -0,0 +1,122 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.priorbox import PriorBoxOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'value': None, 'kind': 'data'},
+ 'pb': {'type': 'PriorBox', 'value': None, 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'data'}
+ }
+
+
+class TestPriorBoxPartialInfer(unittest.TestCase):
+ def test_caffe_priorbox_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'pb'),
+ ('pb', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 384, 19, 19])},
+ 'pb': {
+ 'aspect_ratio': np.array([1]),
+ 'flip': 0,
+ 'min_size': np.array([1]),
+ 'max_size': np.array([1])
+ }
+ })
+ graph.graph['layout'] = 'NCHW'
+ pb_node = Node(graph, 'pb')
+ PriorBoxOp.priorbox_infer(pb_node)
+ exp_shape = np.array([1, 2, 4*19*19*2])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_priorbox_flip_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'pb'),
+ ('pb', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 384, 19, 19])},
+ 'pb': {
+ 'aspect_ratio': np.array([1, 2, 0.5]),
+ 'flip': 1,
+ 'min_size': np.array([1]),
+ 'max_size': np.array([1])
+ }
+ })
+ graph.graph['layout'] = 'NCHW'
+ pb_node = Node(graph, 'pb')
+ PriorBoxOp.priorbox_infer(pb_node)
+ exp_shape = np.array([1, 2, 4*19*19*4])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_tf_priorbox_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'pb'),
+ ('pb', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 19, 19, 384])},
+ 'pb': {
+ 'aspect_ratio': np.array([1]),
+ 'flip': 0,
+ 'min_size': np.array([1]),
+ 'max_size': np.array([1])
+ }
+ })
+ graph.graph['layout'] = 'NHWC'
+ pb_node = Node(graph, 'pb')
+ PriorBoxOp.priorbox_infer(pb_node)
+ exp_shape = np.array([1, 2, 4*19*19*2])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_tf_priorbox_flip_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'pb'),
+ ('pb', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 19, 19, 384])},
+ 'pb': {
+ 'aspect_ratio': np.array([1, 2, 0.5]),
+ 'flip': 1,
+ 'min_size': np.array([1]),
+ 'max_size': np.array([1])
+ }
+ })
+ graph.graph['layout'] = 'NHWC'
+ pb_node = Node(graph, 'pb')
+ PriorBoxOp.priorbox_infer(pb_node)
+ exp_shape = np.array([1, 2, 4*19*19*4])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/proposal_test.py b/model-optimizer/extensions/ops/proposal_test.py
new file mode 100644
index 000000000..0298468c8
--- /dev/null
+++ b/model-optimizer/extensions/ops/proposal_test.py
@@ -0,0 +1,47 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.proposal import ProposalOp
+from mo.front.common.extractors.utils import layout_attrs
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'proposal': {'type': 'proposal', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestProposal(unittest.TestCase):
+ def test_proposal_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'proposal'),
+ ('proposal', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'proposal': {'post_nms_topn': 2, **layout_attrs()}
+ })
+
+ proposal_node = Node(graph, 'proposal')
+ ProposalOp.proposal_infer(proposal_node)
+ exp_shape = np.array([1 * 2, 5])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/psroipooling_test.py b/model-optimizer/extensions/ops/psroipooling_test.py
new file mode 100644
index 000000000..10cdee1cf
--- /dev/null
+++ b/model-optimizer/extensions/ops/psroipooling_test.py
@@ -0,0 +1,84 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.psroipooling import PSROIPoolingOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ 'psroipool': {'type': 'PSROIPooling', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestPSROIPooling(unittest.TestCase):
+ def test_psroipool_infer_nchw(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'psroipool'),
+ ('node_2', 'psroipool'),
+ ('psroipool', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([100, 5])},
+ 'psroipool': {'output_dim': 4, 'group_size': 15}
+ })
+ graph.graph['layout'] = 'NCHW'
+ psroipool_node = Node(graph, 'psroipool')
+ PSROIPoolingOp.psroipooling_infer(psroipool_node)
+ exp_shape = np.array([100, 4, 15, 15])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_psroipool_infer_nhwc(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'psroipool'),
+ ('node_2', 'psroipool'),
+ ('psroipool', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 227, 227, 3])},
+ 'node_2': {'shape': np.array([100, 5])},
+ 'psroipool': {'output_dim': 4, 'group_size': 15}
+ })
+ graph.graph['layout'] = 'NHWC'
+ psroipool_node = Node(graph, 'psroipool')
+ PSROIPoolingOp.psroipooling_infer(psroipool_node)
+ exp_shape = np.array([100, 15, 15, 4])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_psroipool_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'psroipool'),
+ ('node_2', 'psroipool'),
+ ('psroipool', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': None},
+ 'node_2': {'shape': np.array([100, 5])},
+ 'psroipool': {'output_dim': 4, 'group_size': 224}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ psroipool_node = Node(graph, 'psroipool')
+ PSROIPoolingOp.psroipooling_infer(psroipool_node)
+ res_shape = graph.node['node_3']['shape']
+ self.assertIsNone(res_shape)
diff --git a/model-optimizer/extensions/ops/regionyolo_test.py b/model-optimizer/extensions/ops/regionyolo_test.py
new file mode 100644
index 000000000..715163a86
--- /dev/null
+++ b/model-optimizer/extensions/ops/regionyolo_test.py
@@ -0,0 +1,133 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.regionyolo import RegionYoloOp
+from mo.front.common.extractors.utils import layout_attrs
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'region': {'type': 'RegionYolo', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestRegionYOLOCaffe(unittest.TestCase):
+ def test_region_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'region'),
+ ('region', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'region': {'axis': 1, 'end_axis': -1, 'do_softmax': 1, **layout_attrs()}
+ })
+ graph.graph['layout'] = 'NCHW'
+ reorg_node = Node(graph, 'region')
+ RegionYoloOp.regionyolo_infer(reorg_node)
+ exp_shape = np.array([1, 3 * 227 * 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_region_infer_flatten(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'region'),
+ ('region', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'region': {'end_axis': 1, 'axis': 0, 'do_softmax': 1, **layout_attrs()}
+ })
+ graph.graph['layout'] = 'NCHW'
+ reorg_node = Node(graph, 'region')
+ RegionYoloOp.regionyolo_infer(reorg_node)
+ exp_shape = np.array([1 * 3, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_region_infer_flatten_again(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'region'),
+ ('region', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'region': {'end_axis': 2, 'axis': 0, 'do_softmax': 1, **layout_attrs()}
+ })
+ graph.graph['layout'] = 'NCHW'
+ reorg_node = Node(graph, 'region')
+ RegionYoloOp.regionyolo_infer(reorg_node)
+ exp_shape = np.array([1 * 3 * 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_region_infer_do_softmax(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'region'),
+ ('region', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'region': {'do_softmax': 0, 'end_axis': -1, 'axis': 1, 'classes': 80, 'coords': 4,
+ 'mask': np.array([6, 7, 8]), **layout_attrs()}
+ })
+
+ graph.graph['layout'] = 'NCHW'
+ reorg_node = Node(graph, 'region')
+ RegionYoloOp.regionyolo_infer(reorg_node)
+ exp_shape = np.array([1, (80 + 4 + 1) * 3, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+
+class TestRegionYOLOTF(unittest.TestCase):
+ def test_region_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'region'),
+ ('region', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 227, 227, 3])},
+ 'region': {'axis': 1, 'end_axis': -1, 'do_softmax': 1, **layout_attrs()}
+ })
+ graph.graph['layout'] = 'NHWC'
+ reorg_node = Node(graph, 'region')
+ RegionYoloOp.regionyolo_infer(reorg_node)
+ exp_shape = np.array([1, 3 * 227 * 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_region_infer_do_softmax(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'region'),
+ ('region', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 227, 227, 3])},
+ 'region': {'do_softmax': 0, 'end_axis': -1, 'axis': 1, 'classes': 80, 'coords': 4,
+ 'mask': np.array([6, 7, 8]), **layout_attrs()}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ reorg_node = Node(graph, 'region')
+ RegionYoloOp.regionyolo_infer(reorg_node)
+ exp_shape = np.array([1, 227, 227, (80 + 4 + 1) * 3])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/reorgyolo_test.py b/model-optimizer/extensions/ops/reorgyolo_test.py
new file mode 100644
index 000000000..7021fd5bb
--- /dev/null
+++ b/model-optimizer/extensions/ops/reorgyolo_test.py
@@ -0,0 +1,57 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.reorgyolo import ReorgYoloOp
+from mo.front.common.extractors.utils import layout_attrs
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'reorg': {'type': 'ReorgYolo', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+def calculate_reorgyolo_output(input, stride):
+ output = np.full_like(input, -1, dtype=np.int64)
+ output[0] = input[0]
+ output[1] = input[1] * stride ** 2
+ output[2] = np.round(input[2] / stride)
+ output[3] = np.round(input[3] / stride)
+ return output
+
+
+class TestReorgYOLO(unittest.TestCase):
+ def test_reorgyolo_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'reorg'),
+ ('reorg', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'reorg': {'stride': 2,
+ **layout_attrs()}
+ })
+
+ reorg_node = Node(graph, 'reorg')
+ ReorgYoloOp.reorgyolo_infer(reorg_node)
+ exp_shape = calculate_reorgyolo_output(np.array([1, 3, 227, 227]), 2)
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/resample_test.py b/model-optimizer/extensions/ops/resample_test.py
new file mode 100644
index 000000000..bf4c4f0ad
--- /dev/null
+++ b/model-optimizer/extensions/ops/resample_test.py
@@ -0,0 +1,94 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.resample import ResampleOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'resample': {'type': 'Resample', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestResampleOp(unittest.TestCase):
+ def test_tf_resample_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'resample'),
+ ('resample', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'resample': {'antialias': 1,
+ 'height': 384,
+ 'width': 512,
+ 'resample_type': 'LINEAR',
+ 'factor': 1.0}
+ })
+
+ graph.graph['layout'] = 'NCHW'
+ resample_node = Node(graph, 'resample')
+ ResampleOp.resample_infer(resample_node)
+ exp_shape = np.array([1, 3, 384, 512])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_factor_infer(self):
+ factor = 3.0
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'resample'),
+ ('resample', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 224, 227])},
+ 'resample': {'antialias': 1,
+ 'resample_type': 'LINEAR',
+ 'factor': factor}
+ })
+ graph.graph['layout'] = 'NCHW'
+ resample_node = Node(graph, 'resample')
+ ResampleOp.resample_infer(resample_node)
+ exp_shape = np.array([1, 3, 224 * factor, 227 * factor])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_tf_infer(self):
+ new_width = 100
+ new_height = 125
+ new_attrs = nodes_attributes.copy()
+ new_attrs.update({'new_shape': {'value': np.array([new_height, new_width]), 'type': 'Const', 'kind': 'op'}})
+ graph = build_graph(new_attrs,
+ [('node_1', 'resample'),
+ ('new_shape', 'resample'),
+ ('resample', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 224, 227, 3])},
+ 'resample': {'antialias': 1,
+ 'resample_type': 'LINEAR',
+ 'factor': 1.0,
+ 'fw': 'tf'}
+ })
+ graph.graph['layout'] = 'NHWC'
+ resample_node = Node(graph, 'resample')
+ ResampleOp.resample_infer(resample_node)
+ exp_shape = np.array([1, new_height, new_width, 3])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/select_test.py b/model-optimizer/extensions/ops/select_test.py
new file mode 100644
index 000000000..15578d30e
--- /dev/null
+++ b/model-optimizer/extensions/ops/select_test.py
@@ -0,0 +1,114 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+import numpy as np
+
+from extensions.ops.select import Select
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph_with_attrs, compare_graphs
+
+
+class TestSelect(unittest.TestCase):
+ nodes = [
+ ('than', {'value': np.ones((2, 2)), 'kind': 'data', 'executable': True, 'shape': np.array([2, 2])}),
+ ('else', {'value': np.zeros((2, 2)), 'kind': 'data', 'executable': True, 'shape': np.array([2, 2])}),
+ ('condition', {'value': None, 'kind': 'data', 'executable': True}),
+ ('select', {'type': 'Select', 'kind': 'op', 'op': 'Select'}),
+ ('select_output', {'value': None, 'kind': 'data', 'executable': True, 'shape': None}),
+ ]
+ edges = [
+ ('condition', 'select', {'in': 0}),
+ ('than', 'select', {'in': 1}),
+ ('else', 'select', {'in': 2}),
+ ('select', 'select_output', {'out': 0}),
+ ]
+
+ def test_select_infer_no_condition(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges)
+
+ # We should propagate only shapes
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ update_nodes_attributes=[('select_output', {'shape': np.array([2, 2])})])
+
+ tested_class = Select(graph=graph, attrs={})
+
+ node = Node(graph, 'select')
+ tested_class.infer(node)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'select_output', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_select_infer_condition_true(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges,
+ update_nodes_attributes=[('condition', {'value': np.array([True])})])
+
+ # We should propagate shapes and values
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ update_nodes_attributes=[('select_output', {'shape': np.array([2, 2]),
+ 'value': np.ones((2,2))})])
+
+ tested_class = Select(graph=graph, attrs={})
+
+ node = Node(graph, 'select')
+ tested_class.infer(node)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'select_output', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_select_infer_condition_false(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges,
+ update_nodes_attributes=[('condition', {'value': np.array([False])})])
+
+ # We should propagate shapes and values
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes,
+ edges_with_attrs=self.edges,
+ update_nodes_attributes=[('select_output', {'shape': np.array([2, 2]),
+ 'value': np.zeros((2, 2))})])
+
+ tested_class = Select(graph=graph, attrs={})
+
+ node = Node(graph, 'select')
+ tested_class.infer(node)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'select_output', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_select_infer_assert_shapes(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges,
+ update_nodes_attributes=[('else', {'shape': np.array([3,3]), 'value':np.zeros((3,3))})])
+
+ tested_class = Select(graph=graph, attrs={})
+
+ node = Node(graph, 'select')
+ with self.assertRaisesRegex(AssertionError, "TensorFlow \'Select\' operation has 3 inputs: \'condition\',"
+ " \'then\' and \'else\' tensors.\'then\' and \'else\' tensors"
+ " must have the same shape by TensorFlow reference"):
+ tested_class.infer(node)
+
+ def test_select_infer_assert_condition_bool(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges,
+ update_nodes_attributes=[('condition', {'value': np.array([3])})])
+
+ tested_class = Select(graph=graph, attrs={})
+
+ node = Node(graph, 'select')
+ with self.assertRaisesRegex(AssertionError, "TensorFlow \'Select\' operation has 3 inputs: \'condition\',"
+ " \'then\' and \'else\' tensors. Value of \'condition\' tensor"
+ " must be boolen by TensorFlow reference"):
+ tested_class.infer(node) \ No newline at end of file
diff --git a/model-optimizer/extensions/ops/simplernms_test.py b/model-optimizer/extensions/ops/simplernms_test.py
new file mode 100644
index 000000000..08cbf5343
--- /dev/null
+++ b/model-optimizer/extensions/ops/simplernms_test.py
@@ -0,0 +1,57 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.simplernms import SimplerNMSOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'SimplerNMS_1': {'type': 'SimplerNMS', 'kind': 'op'},
+ 'node_1': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestSimplerNMSInfer(unittest.TestCase):
+ def test_simplernms_infer_ideal(self):
+ graph = build_graph(nodes_attributes,
+ [('SimplerNMS_1', 'node_1')],
+ {'node_1': {'is_output': True, 'shape': None},
+ 'SimplerNMS_1': {'feat_stride': 16, 'post_nms_topn': 150, 'scale': [1, 2, 3]}
+ })
+
+ simplernms_node = Node(graph, 'SimplerNMS_1')
+
+ SimplerNMSOp.simplernms_infer(simplernms_node)
+ exp_shape = np.array([150, 5])
+ res_shape = graph.node['node_1']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+ self.assertEqual(simplernms_node.scale, ['1', '2', '3'])
+
+ def test_simplernms_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('SimplerNMS_1', 'node_1')],
+ {'node_1': {'is_output': True, 'shape': None},
+ 'SimplerNMS_1': {'feat_stride': 12, 'post_nms_topn': 150, 'scale': [1, 2, 3]}
+ })
+
+ simplernms_node = Node(graph, 'SimplerNMS_1')
+
+ SimplerNMSOp.simplernms_infer(simplernms_node)
+ self.assertIsNone(graph.node['node_1']['shape'])
diff --git a/model-optimizer/extensions/ops/spatial_transformer_test.py b/model-optimizer/extensions/ops/spatial_transformer_test.py
new file mode 100644
index 000000000..86b7ec26c
--- /dev/null
+++ b/model-optimizer/extensions/ops/spatial_transformer_test.py
@@ -0,0 +1,71 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from extensions.ops.spatial_transformer import SpatialTransformOp
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ 'st': {'type': 'SpatialTransform', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'}
+ }
+
+
+class TestSpatialTransformInfer(unittest.TestCase):
+ def test_sp_transform_concat_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'st'),
+ ('node_2', 'st'),
+ ('st', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 227, 227])},
+ 'st': {}
+ })
+
+ st_node = Node(graph, 'st')
+ SpatialTransformOp.sp_infer(st_node)
+ exp_shape = np.array([1, 3, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_sp_transform_with_output_params_infer(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('node_1', 'st'),
+ ('node_2', 'st'),
+ ('st', 'node_3')],
+ {
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 227, 227])},
+ 'st': {'output_H': 200, 'output_W': 15}
+ })
+
+ st_node = Node(graph, 'st')
+ SpatialTransformOp.sp_infer(st_node)
+ exp_shape = np.array([1, 3, 200, 15])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/extensions/ops/switch_test.py b/model-optimizer/extensions/ops/switch_test.py
new file mode 100644
index 000000000..c5bb75989
--- /dev/null
+++ b/model-optimizer/extensions/ops/switch_test.py
@@ -0,0 +1,234 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import Mock, call
+
+import numpy as np
+
+from extensions.ops.switch import Switch
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph_with_edge_attrs, compare_graphs, build_graph_with_attrs
+
+
+class TestSwitch(unittest.TestCase):
+ def test_switch_infer_with_condition(self):
+ nodes = [
+ ('tensor', {'value': np.zeros((3, 3)), 'kind': 'data', 'executable': True, 'shape': np.array([3, 3])}),
+ ('pred_id', {'value': True, 'kind': 'data', 'executable': True}),
+ ('switch', {'type': 'Switch', 'kind': 'op', 'op': 'Switch'}),
+ ('switch_data_0', {'value': None, 'kind': 'data', 'executable': True}),
+ ('switch_data_1', {'value': None, 'kind': 'data', 'executable': True})
+ ]
+ edges = [
+ ('tensor', 'switch', {'in': 0}),
+ ('pred_id', 'switch', {'in': 1}),
+ ('switch', 'switch_data_0', {'out': 0}),
+ ('switch', 'switch_data_1', {'out': 1})
+ ]
+ graph = build_graph_with_attrs(nodes_with_attrs=nodes, edges_with_attrs=edges)
+
+ # We should propagate shapes and values
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=nodes,
+ edges_with_attrs=edges,
+ update_nodes_attributes=[('switch_data_0', {'shape': np.array([3, 3]),
+ 'value': np.zeros((3,3))}),
+ ('switch_data_1', {'shape': np.array([3, 3]),
+ 'value': np.zeros((3,3))})])
+
+ tested_class = Switch(graph=graph, attrs={})
+
+ node = Node(graph, 'switch')
+ tested_class.infer(node)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'switch_data_0', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_switch_infer_no_condition(self):
+ nodes = [
+ ('tensor', {'value': None, 'kind': 'data', 'executable': True, 'shape': np.array([1, 2, 1])}),
+ ('pred_id', {'value': None, 'kind': 'data', 'executable': True}),
+ ('switch', {'type': 'Switch', 'kind': 'op', 'op': 'Switch'}),
+ ('switch_data_0', {'value': None, 'kind': 'data', 'executable': True}),
+ ('switch_data_1', {'value': None, 'kind': 'data', 'executable': True})
+ ]
+ edges = [
+ ('tensor', 'switch', {'in': 0}),
+ ('pred_id', 'switch', {'in': 1}),
+ ('switch', 'switch_data_0', {'out': 0}),
+ ('switch', 'switch_data_1', {'out': 1})
+ ]
+ graph = build_graph_with_attrs(nodes_with_attrs=nodes, edges_with_attrs=edges)
+
+ # We should propagate only shapes
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=nodes,
+ edges_with_attrs=edges,
+ update_nodes_attributes=[('switch_data_0', {'shape': np.array([1, 2, 1])}),
+ ('switch_data_1', {'shape': np.array([1, 2, 1])})])
+
+ tested_class = Switch(graph=graph, attrs={})
+
+ node = Node(graph, 'switch')
+ tested_class.infer(node)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'switch_data_0', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_switch_cf_infer_no_condition(self):
+ me_mock = Mock()
+ nodes = {
+ 'tensor': {'value': True, 'kind': 'data', 'executable': True},
+ 'pred_id': {'value': None, 'kind': 'data', 'executable': True},
+ 'switch': {'type': 'Switch', 'kind': 'op', 'op': 'Switch'},
+ 'switch_data_0': {'value': None, 'kind': 'data', 'executable': True},
+ 'switch_data_1': {'value': None, 'kind': 'data', 'executable': True}
+ }
+ edges = [
+ ('tensor', 'switch', {'in': 0}),
+ ('pred_id', 'switch', {'in': 1}),
+ ('switch', 'switch_data_0', {'out': 0}),
+ ('switch', 'switch_data_1', {'out': 1})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+
+ tested_class = Switch(graph=graph, attrs={})
+ node = Node(graph, 'switch')
+ tested_class.control_flow_infer(node, True, me_mock)
+ # In this case we should mark all ports as executable
+ me_mock.assert_has_calls([call('switch_data_0', True), call('switch_data_1', True)], any_order=True)
+
+ def test_switch_cf_true_both_ports(self):
+ me_mock = Mock()
+ nodes = {
+ 'tensor': {'value': True, 'kind': 'data', 'executable': True},
+ 'pred_id': {'value': np.array(True), 'kind': 'data', 'executable': True},
+ 'switch': {'type': 'Switch', 'kind': 'op', 'op': 'Switch'},
+ 'switch_data_0': {'value': None, 'kind': 'data', 'executable': True},
+ 'switch_data_1': {'value': None, 'kind': 'data', 'executable': True}
+ }
+ edges = [
+ ('tensor', 'switch', {'in': 0}),
+ ('pred_id', 'switch', {'in': 1}),
+ ('switch', 'switch_data_0', {'out': 0}),
+ ('switch', 'switch_data_1', {'out': 1})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ tested_class = Switch(graph=graph, attrs={})
+ node = Node(graph, 'switch')
+ tested_class.control_flow_infer(node, True, me_mock)
+ me_mock.assert_has_calls([call('switch_data_0', False), call('switch_data_1', True)], any_order=True)
+
+ def test_switch_cf_false_both_ports(self):
+ me_mock = Mock()
+
+ nodes = {
+ 'tensor': {'value': True, 'kind': 'data', 'executable': True},
+ 'pred_id': {'value': np.array(False), 'kind': 'data', 'executable': True},
+ 'switch': {'type': 'Switch', 'kind': 'op', 'op': 'Switch'},
+ 'switch_data_0': {'value': None, 'kind': 'data', 'executable': True},
+ 'switch_data_1': {'value': None, 'kind': 'data', 'executable': True}
+ }
+ edges = [
+ ('tensor', 'switch', {'in': 0}),
+ ('pred_id', 'switch', {'in': 1}),
+ ('switch', 'switch_data_0', {'out': 0}),
+ ('switch', 'switch_data_1', {'out': 1})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ tested_class = Switch(graph=graph, attrs={})
+ node = Node(graph, 'switch')
+ tested_class.control_flow_infer(node, True, me_mock)
+ me_mock.assert_has_calls([call('switch_data_0', True), call('switch_data_1', False)], any_order=True)
+
+ def test_switch_cf_true_one_exec_port(self):
+ me_mock = Mock()
+
+ nodes = {
+ 'tensor': {'value': True, 'kind': 'data', 'executable': True},
+ 'pred_id': {'value': np.array(True), 'kind': 'data', 'executable': True},
+ 'switch': {'type': 'Switch', 'kind': 'op', 'op': 'Switch'},
+ 'switch_data_1': {'value': None, 'kind': 'data', 'executable': True}
+ }
+ edges = [
+ ('tensor', 'switch', {'in': 0}),
+ ('pred_id', 'switch', {'in': 1}),
+ ('switch', 'switch_data_1', {'out': 1})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ tested_class = Switch(graph=graph, attrs={})
+ node = Node(graph, 'switch')
+ tested_class.control_flow_infer(node, True, me_mock)
+ me_mock.assert_has_calls([call('switch_data_1', True)], any_order=True)
+
+ def test_switch_cf_false_one_exec_port(self):
+ me_mock = Mock()
+
+ nodes = {
+ 'tensor': {'value': True, 'kind': 'data', 'executable': True},
+ 'pred_id': {'value': np.array(False), 'kind': 'data', 'executable': True},
+ 'switch': {'type': 'Switch', 'kind': 'op', 'op': 'Switch'},
+ 'switch_data_0': {'value': None, 'kind': 'data', 'executable': True},
+ }
+ edges = [
+ ('tensor', 'switch', {'in': 0}),
+ ('pred_id', 'switch', {'in': 1}),
+ ('switch', 'switch_data_0', {'out': 0}),
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ tested_class = Switch(graph=graph, attrs={})
+ node = Node(graph, 'switch')
+ tested_class.control_flow_infer(node, True, me_mock)
+ me_mock.assert_has_calls([call('switch_data_0', True)], any_order=True)
+
+ def test_switch_cf_true_no_exec(self):
+ me_mock = Mock()
+
+ nodes = {
+ 'tensor': {'value': True, 'kind': 'data', 'executable': True},
+ 'pred_id': {'value': np.array(True), 'kind': 'data', 'executable': True},
+ 'switch': {'type': 'Switch', 'kind': 'op', 'op': 'Switch'},
+ 'switch_data_0': {'value': None, 'kind': 'data', 'executable': True}
+ }
+ edges = [
+ ('tensor', 'switch', {'in': 0}),
+ ('pred_id', 'switch', {'in': 1}),
+ ('switch', 'switch_data_0', {'out': 0}),
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ tested_class = Switch(graph=graph, attrs={})
+ node = Node(graph, 'switch')
+ tested_class.control_flow_infer(node, True, me_mock)
+ me_mock.assert_has_calls([call('switch_data_0', False)], any_order=True)
+
+ def test_switch_cf_false_no_exec(self):
+ me_mock = Mock()
+
+ nodes = {
+ 'tensor': {'value': True, 'kind': 'data', 'executable': True},
+ 'pred_id': {'value': np.array(False), 'kind': 'data', 'executable': True},
+ 'switch': {'type': 'Switch', 'kind': 'op', 'op': 'Switch'},
+ 'switch_data_1': {'value': None, 'kind': 'data', 'executable': True}
+ }
+ edges = [
+ ('tensor', 'switch', {'in': 0}),
+ ('pred_id', 'switch', {'in': 1}),
+ ('switch', 'switch_data_1', {'out': 1})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ tested_class = Switch(graph=graph, attrs={})
+ node = Node(graph, 'switch')
+ tested_class.control_flow_infer(node, True, me_mock)
+ me_mock.assert_has_calls([call('switch_data_1', False)], any_order=True)
diff --git a/model-optimizer/mo/back/ie_ir_ver_2/emitter_test.py b/model-optimizer/mo/back/ie_ir_ver_2/emitter_test.py
new file mode 100644
index 000000000..44830dd10
--- /dev/null
+++ b/model-optimizer/mo/back/ie_ir_ver_2/emitter_test.py
@@ -0,0 +1,74 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import MagicMock
+from xml.etree.ElementTree import Element, tostring
+
+import numpy as np
+
+from mo.back.ie_ir_ver_2.emitter import soft_get, xml_shape
+from mo.utils.error import Error
+
+expected_result = b'<net><dim>2</dim><dim>10</dim><dim>50</dim><dim>50</dim></net>'
+
+
+class TestEmitter(unittest.TestCase):
+ def test_xml_shape(self):
+ net = Element('net')
+ xml_shape(np.array([2, 10, 50, 50], dtype=np.int64), net)
+ self.assertEqual(tostring(net), expected_result)
+
+ def test_xml_shape_float_values(self):
+ net = Element('net')
+ xml_shape(np.array([2.0, 10.0, 50.0, 50.0], dtype=np.float32), net)
+ self.assertEqual(tostring(net), expected_result)
+
+ def test_xml_shape_non_integer_values(self):
+ net = Element('net')
+ with self.assertRaises(Error):
+ xml_shape(np.array([2.0, 10.0, 50.0, 50.5], dtype=np.float32), net)
+
+ def test_xml_shape_negative_values(self):
+ net = Element('net')
+ with self.assertRaises(Error):
+ xml_shape(np.array([2, 10, 50, -50], dtype=np.int64), net)
+
+ def test_xml_shape_zero_values(self):
+ net = Element('net')
+ with self.assertRaises(Error):
+ xml_shape(np.array([2, 0, 50, 50], dtype=np.int64), net)
+
+
+class TestSoftGet(unittest.TestCase):
+
+ def test_node(self):
+ node = MagicMock()
+ node.soft_get = lambda attr: attr
+ self.assertEqual(soft_get(node, 'string'), 'string')
+
+ def test_not_callable(self):
+ node = MagicMock()
+ node.soft_get = 'foo'
+ self.assertEqual(soft_get(node, 'string'), '<SUB-ELEMENT>')
+
+ def test_not_node_1(self):
+ node = {'soft_get': lambda attr: attr}
+ self.assertEqual(soft_get(node, 'string'), '<SUB-ELEMENT>')
+
+ def test_not_node_2(self):
+ node = 'something-else'
+ self.assertEqual(soft_get(node, 'string'), '<SUB-ELEMENT>')
diff --git a/model-optimizer/mo/front/caffe/custom_layers_mapping_test.py b/model-optimizer/mo/front/caffe/custom_layers_mapping_test.py
new file mode 100644
index 000000000..84ce9b5cf
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/custom_layers_mapping_test.py
@@ -0,0 +1,63 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+from google.protobuf import text_format
+
+from mo.front.caffe.custom_layers_mapping import proto_extractor
+from mo.front.caffe.proto import caffe_pb2
+
+
+class TestCustomLayerMapping(unittest.TestCase):
+ def test_extractor_custom_layer(self):
+ expected_conv_params = {
+ 'num_output': 64,
+ 'pad': 1,
+ 'kernel_size': 3,
+ 'stride': 1,
+ 'bias_term': True,
+ 'axis': 1,
+ 'engine': 'caffe.ConvolutionParameter.DEFAULT',
+ 'group': 1,
+ 'force_nd_im2col': False,
+ 'pad_h': 0,
+ 'pad_w': 0
+ }
+ layer = """
+ name: "conv"
+ type: "Convolution"
+ bottom: "input"
+ top: "conv"
+ convolution_param {
+ num_output: 64
+ pad: 1
+ kernel_size: 3
+ stride: 1
+ }
+ """
+ mapping = {
+ 'NativeType': 'Convolution',
+ 'hasParam': 'true',
+ 'protoParamName': 'convolution_param'
+ }
+ proto = caffe_pb2.LayerParameter()
+ text_format.Merge(layer, proto)
+ attrs = proto_extractor(proto, None, mapping, False, False)
+ for key, val in expected_conv_params.items():
+ if key == 'bias_term' or key == 'force_nd_im2col':
+ self.assertTrue(str(int(val)) == attrs[key])
+ else:
+ self.assertTrue(str(val) == attrs[key])
diff --git a/model-optimizer/mo/front/caffe/extractor_test.py b/model-optimizer/mo/front/caffe/extractor_test.py
new file mode 100644
index 000000000..b5b292525
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractor_test.py
@@ -0,0 +1,124 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from mo.front.caffe.extractor import check_phase, register_caffe_python_extractor
+from mo.front.extractor import CaffePythonFrontExtractorOp
+from mo.graph.graph import Node
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'kind': 'op'},
+ 'node_2': {'type': 'Identity', 'kind': 'op'}}
+
+
+class TestExtractor(unittest.TestCase):
+ def test_check_phase_train_phase(self):
+ phase_param = {
+ 'phase': 0
+ }
+
+ include_param = {
+ 'include': [FakeMultiParam(phase_param)]
+ }
+
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {
+ 'node_1': {'pb': FakeMultiParam(include_param)}
+ })
+
+ node = Node(graph, 'node_1')
+ res = check_phase(node)
+ exp_res = {'phase': 0}
+ self.assertEqual(res, exp_res)
+
+ def test_check_phase_test_phase(self):
+ phase_param = {
+ 'phase': 1
+ }
+
+ include_param = {
+ 'include': [FakeMultiParam(phase_param)]
+ }
+
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {
+ 'node_1': {'pb': FakeMultiParam(include_param)}
+ })
+
+ node = Node(graph, 'node_1')
+ res = check_phase(node)
+ exp_res = {'phase': 1}
+ self.assertEqual(res, exp_res)
+
+ def test_check_phase_no_phase(self):
+ phase_param = {}
+
+ include_param = {
+ 'include': [FakeMultiParam(phase_param)]
+ }
+
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {
+ 'node_1': {'pb': FakeMultiParam(include_param)}
+ })
+
+ node = Node(graph, 'node_1')
+ res = check_phase(node)
+ exp_res = {}
+ self.assertEqual(res, exp_res)
+
+ def test_check_phase_no_include(self):
+ include_param = {}
+
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {
+ 'node_1': {'pb': FakeMultiParam(include_param)}
+ })
+
+ node = Node(graph, 'node_1')
+ res = check_phase(node)
+ exp_res = {}
+ self.assertEqual(res, exp_res)
+
+ def test_check_phase_no_pb(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {})
+
+ node = Node(graph, 'node_1')
+ res = check_phase(node)
+ exp_res = {}
+ self.assertEqual(res, exp_res)
+
+ @patch('mo.ops.activation.Activation')
+ def test_register_caffe_python_extractor_by_name(self, op_mock):
+ op_mock.op = 'TestLayer'
+ name = 'myTestLayer'
+ register_caffe_python_extractor(op_mock, name)
+ self.assertIn(name, CaffePythonFrontExtractorOp.registered_ops)
+
+ @patch('mo.ops.activation.Activation')
+ def test_register_caffe_python_extractor_by_op(self, op_mock):
+ op_mock.op = 'TestLayer'
+ register_caffe_python_extractor(op_mock)
+ self.assertIn(op_mock.op, CaffePythonFrontExtractorOp.registered_ops)
diff --git a/model-optimizer/mo/front/caffe/extractors/batchnorm_test.py b/model-optimizer/mo/front/caffe/extractors/batchnorm_test.py
new file mode 100644
index 000000000..eeb441d2e
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/batchnorm_test.py
@@ -0,0 +1,129 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.caffe.extractors.batchnorm import batch_norm_ext
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.utils.unittest.extractors import FakeParam, FakeModelLayer
+
+
+class FakeBNProtoLayer:
+ def __init__(self, eps):
+ self.batch_norm_param = FakeParam('eps', eps)
+
+
+class TestShapesParsing(unittest.TestCase):
+ def test_bn_ext_no_ml_no_pb(self):
+ self.assertRaises(AssertionError, batch_norm_ext, None, None)
+
+ def test_bn_ext_no_ml(self):
+ res = batch_norm_ext(FakeBNProtoLayer(10), None)
+ exp_res = {
+ 'op': 'BatchNormalization',
+ 'type': 'BatchNormalization',
+ 'epsilon': 10,
+ 'infer': copy_shape_infer
+ }
+ self.assertEqual(res, exp_res)
+
+ def test_bn_ext_ml_one_blob(self):
+ self.assertRaises(AssertionError, batch_norm_ext, FakeBNProtoLayer(10), FakeModelLayer([np.array([1, 2])]))
+
+ def test_bn_ext_ml_two_blobs(self):
+ mean_blob = np.array([1., 2.])
+ variance_blob = np.array([3., 4.])
+ blobs = [mean_blob, variance_blob]
+ res = batch_norm_ext(FakeBNProtoLayer(10),
+ FakeModelLayer(blobs))
+ exp_res = {
+ 'type': 'BatchNormalization',
+ 'epsilon': 10,
+ 'infer': copy_shape_infer,
+ 'mean': mean_blob,
+ 'variance': variance_blob,
+ 'embedded_inputs': [
+ (1, 'mean', {
+ 'bin': 'biases'
+ }),
+ (2, 'variance', {
+ 'bin': 'weights'
+ })
+ ]
+ }
+ for i in exp_res:
+ if i in ('mean', 'variance'):
+ np.testing.assert_array_equal(res[i], exp_res[i])
+ else:
+ self.assertEqual(res[i], exp_res[i])
+
+ def test_bn_ext_ml_three_blobs(self):
+ mean_blob = np.array([1., 2.])
+ variance_blob = np.array([3., 4.])
+ scale_blob = np.array([5., ])
+ blobs = [mean_blob, variance_blob, scale_blob]
+ res = batch_norm_ext(FakeBNProtoLayer(10),
+ FakeModelLayer(blobs))
+ exp_res = {
+ 'type': 'BatchNormalization',
+ 'epsilon': 10,
+ 'infer': copy_shape_infer,
+ 'mean': mean_blob * 0.2,
+ 'variance': variance_blob * 0.2,
+ 'embedded_inputs': [
+ (1, 'mean', {
+ 'bin': 'biases'
+ }),
+ (2, 'variance', {
+ 'bin': 'weights'
+ })
+ ]
+ }
+ for i in exp_res:
+ if i in ('mean', 'variance'):
+ np.testing.assert_array_equal(res[i], exp_res[i])
+ else:
+ self.assertEqual(res[i], exp_res[i])
+
+ def test_bn_ext_ml_three_blobs_zero_scale(self):
+ mean_blob = np.array([1., 2.])
+ variance_blob = np.array([3., 4.])
+ scale_blob = np.array([0., ])
+ blobs = [mean_blob, variance_blob, scale_blob]
+ res = batch_norm_ext(FakeBNProtoLayer(10),
+ FakeModelLayer(blobs))
+ exp_res = {
+ 'type': 'BatchNormalization',
+ 'epsilon': 10,
+ 'infer': copy_shape_infer,
+ 'mean': mean_blob * 0.,
+ 'variance': variance_blob * 0.,
+ 'embedded_inputs': [
+ (1, 'mean', {
+ 'bin': 'biases'
+ }),
+ (2, 'variance', {
+ 'bin': 'weights'
+ })
+ ]
+ }
+ for i in exp_res:
+ if i in ('mean', 'variance'):
+ np.testing.assert_array_equal(res[i], exp_res[i])
+ else:
+ self.assertEqual(res[i], exp_res[i])
diff --git a/model-optimizer/mo/front/caffe/extractors/concat_test.py b/model-optimizer/mo/front/caffe/extractors/concat_test.py
new file mode 100644
index 000000000..117ce04f6
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/concat_test.py
@@ -0,0 +1,37 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.caffe.extractors.concat import concat_ext
+from mo.front.common.partial_infer.concat import concat_infer
+from mo.utils.unittest.extractors import FakeParam
+
+
+class FakeProtoLayer:
+ def __init__(self, axis):
+ self.concat_param = FakeParam('axis', axis)
+
+
+class TestConcat(unittest.TestCase):
+ def test_concat(self):
+ res = concat_ext(FakeProtoLayer(10), None)
+ exp_res = {
+ 'axis': 10,
+ 'infer': concat_infer,
+ 'type': 'Concat'
+ }
+ self.assertEqual(res, exp_res)
diff --git a/model-optimizer/mo/front/caffe/extractors/crop_test.py b/model-optimizer/mo/front/caffe/extractors/crop_test.py
new file mode 100644
index 000000000..9405e70fa
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/crop_test.py
@@ -0,0 +1,66 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from mo.front.caffe.extractors.crop import CropFrontExtractor
+from mo.front.common.partial_infer.crop import crop_infer
+from mo.ops.crop import Crop
+from mo.ops.op import Op
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+
+
+class FakeCropProtoLayer:
+ def __init__(self, val):
+ self.crop_param = val
+
+
+class TestCropExt(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ Op.registered_ops['Crop'] = Crop
+
+ def test_da_no_pb_no_ml(self):
+ self.assertRaises(AttributeError, CropFrontExtractor.extract, None)
+
+ @patch('mo.front.caffe.collect_attributes')
+ def test_crop_ext(self, collect_attributes_mock):
+ params = {
+ 'axis': 0,
+ 'offset': 0,
+ }
+ collect_attributes_mock.return_value = {
+ **params,
+ 'test': 54,
+ 'test2': 'test3'
+ }
+ fake_pl = FakeCropProtoLayer(FakeMultiParam(params))
+ fake_node = FakeNode(fake_pl, None)
+
+ CropFrontExtractor.extract(fake_node)
+
+ exp_res = {
+ 'type': 'Crop',
+ 'axis': 0,
+ 'offset': 0,
+ 'dim': None, # set in infer
+ 'infer': crop_infer
+ }
+
+ for key in exp_res.keys():
+ self.assertEqual(exp_res[key], fake_node[key])
diff --git a/model-optimizer/mo/front/caffe/extractors/eltwise_test.py b/model-optimizer/mo/front/caffe/extractors/eltwise_test.py
new file mode 100644
index 000000000..e077c4243
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/eltwise_test.py
@@ -0,0 +1,93 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from mo.front.caffe.extractors.eltwise import eltwise_ext
+from mo.utils.unittest.extractors import FakeMultiParam
+
+
+class FakeProtoLayer:
+ def __init__(self, operation, coeff=[1]):
+ self.eltwise_param = FakeMultiParam({'operation': operation,
+ 'coeff': coeff})
+
+
+class TestEltwise(unittest.TestCase):
+ @patch('mo.front.caffe.extractors.eltwise.eltwise_infer')
+ def test_eltwise_op_mul(self, eltwise_infer_mock):
+ eltwise_infer_mock.return_value = {}
+ res = eltwise_ext(FakeProtoLayer(0), None)
+ exp_res = {
+ 'op': 'Mul',
+ 'operation': 'mul',
+ 'infer': None
+ }
+
+ for i in exp_res.keys():
+ if i == 'infer':
+ res['infer'](None)
+ args = eltwise_infer_mock.call_args
+ actual_lambda = args[0][1]
+ self.assertTrue(eltwise_infer_mock.called)
+ self.assertEqual(actual_lambda(3, 5), 3 * 5)
+ else:
+ self.assertEqual(res[i], exp_res[i])
+
+ @patch('mo.front.caffe.extractors.eltwise.eltwise_infer')
+ def test_eltwise_op_add(self, eltwise_infer_mock):
+ eltwise_infer_mock.return_value = {}
+ res = eltwise_ext(FakeProtoLayer(1, coeff=[0.39]), None)
+ exp_res = {
+ 'op': 'Add',
+ 'operation': 'sum',
+ 'coeff': '0.39',
+ 'infer': None
+ }
+
+ for i in exp_res.keys():
+ if i == 'infer':
+ res['infer'](None)
+ args = eltwise_infer_mock.call_args
+ actual_lambda = args[0][1]
+ self.assertTrue(eltwise_infer_mock.called)
+ self.assertEqual(actual_lambda(3, 5), 3 + 5)
+ else:
+ self.assertEqual(res[i], exp_res[i])
+
+ @patch('mo.front.caffe.extractors.eltwise.eltwise_infer')
+ def test_eltwise_op_max(self, eltwise_infer_mock):
+ eltwise_infer_mock.return_value = {}
+ res = eltwise_ext(FakeProtoLayer(2), None)
+ exp_res = {
+ 'op': 'Max',
+ 'operation': 'max',
+ 'infer': None
+ }
+
+ for i in exp_res.keys():
+ if i == 'infer':
+ res['infer'](None)
+ args = eltwise_infer_mock.call_args
+ actual_lambda = args[0][1]
+ self.assertTrue(eltwise_infer_mock.called)
+ self.assertEqual(actual_lambda(3, 5), 5)
+ else:
+ self.assertEqual(res[i], exp_res[i])
+
+ def test_eltwise_op_exeption(self):
+ self.assertRaises(Exception, eltwise_ext, FakeProtoLayer(4), None)
diff --git a/model-optimizer/mo/front/caffe/extractors/elu_test.py b/model-optimizer/mo/front/caffe/extractors/elu_test.py
new file mode 100644
index 000000000..c482888bd
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/elu_test.py
@@ -0,0 +1,53 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from unittest.mock import patch
+
+from mo.front.caffe.extractors.elu import ELUFrontExtractor
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+
+
+class FakeProtoLayer:
+ def __init__(self, val):
+ self.elu_param = val
+
+
+class TestElu(unittest.TestCase):
+ @patch('mo.front.caffe.extractors.elu.collect_attributes')
+ def test_elu_ext(self, collect_attrs_mock):
+ params = {
+ 'alpha': 4
+ }
+ collect_attrs_mock.return_value = {
+ **params,
+ 'test': 54,
+ 'test2': 'test3'
+ }
+
+ fn = FakeNode(FakeProtoLayer(FakeMultiParam(params)), None)
+ ELUFrontExtractor.extract(fn)
+
+ exp_res = {
+ 'type': 'Activation',
+ 'operation': 'elu',
+ 'alpha': 4
+ }
+
+ for i in exp_res:
+ self.assertEqual(fn[i], exp_res[i])
diff --git a/model-optimizer/mo/front/caffe/extractors/inner_product_test.py b/model-optimizer/mo/front/caffe/extractors/inner_product_test.py
new file mode 100644
index 000000000..44501c390
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/inner_product_test.py
@@ -0,0 +1,61 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.caffe.extractors.inner_product import inner_product_ext
+from mo.front.common.partial_infer.inner_product import caffe_inner_product
+from mo.utils.unittest.extractors import FakeMultiParam, FakeModelLayer
+
+
+class FakeProtoLayer:
+ def __init__(self, val):
+ self.inner_product_param = val
+
+
+class TestInnerProduct(unittest.TestCase):
+ def test_inner_product_ext(self):
+ params = {
+ 'num_output': 10,
+ 'bias_term': True
+ }
+ mean_blob = np.array([1., 2.])
+ variance_blob = np.array([3., 4.])
+ blobs = [mean_blob, variance_blob]
+ res = inner_product_ext(FakeProtoLayer(FakeMultiParam(params)),
+ FakeModelLayer(blobs))
+ exp_res = {
+ 'type': 'FullyConnected',
+ 'out-size': 10,
+ 'infer': caffe_inner_product,
+ 'weights': mean_blob,
+ 'biases': variance_blob,
+ 'embedded_inputs': [
+ (1, 'weights', {
+ 'bin': 'weights'
+ }),
+ (2, 'biases', {
+ 'bin': 'biases'
+ })
+ ]
+ }
+ for i in exp_res:
+ if i in ('weights', 'biases'):
+ np.testing.assert_array_equal(res[i], exp_res[i])
+ else:
+ self.assertEqual(res[i], exp_res[i])
diff --git a/model-optimizer/mo/front/caffe/extractors/input_test.py b/model-optimizer/mo/front/caffe/extractors/input_test.py
new file mode 100644
index 000000000..37d1fc149
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/input_test.py
@@ -0,0 +1,57 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from mo.front.caffe.extractors.input import input_ext, global_input_ext
+from mo.utils.unittest.extractors import FakeParam
+
+
+class FakeProtoLayer:
+ def __init__(self, shape):
+ self.input_param = FakeParam('shape', shape)
+
+
+class TestInput(unittest.TestCase):
+ @patch('mo.front.caffe.extractors.input.single_output_infer')
+ def test_input_ext(self, single_output_infer_mock):
+ single_output_infer_mock.return_value = {}
+ shape = [FakeParam('dim', 1)]
+ res = input_ext(FakeProtoLayer(shape), None)
+ exp_res = {
+ 'op': 'Placeholder',
+ 'shape': [1],
+ 'infer': None
+ }
+ for i in exp_res.keys():
+ if i == 'infer':
+ res['infer'](None)
+ self.assertTrue(single_output_infer_mock.called)
+
+ @patch('mo.front.caffe.extractors.input.single_output_infer')
+ def test_global_input_ext(self, single_output_infer_mock):
+ single_output_infer_mock.return_value = {}
+ res = global_input_ext(None, None)
+ exp_res = {
+ 'op': 'Placeholder',
+ 'type': 'input',
+ 'infer': None
+ }
+ for i in exp_res.keys():
+ if i == 'infer':
+ res['infer'](None)
+ self.assertTrue(single_output_infer_mock.called)
diff --git a/model-optimizer/mo/front/caffe/extractors/lrn_test.py b/model-optimizer/mo/front/caffe/extractors/lrn_test.py
new file mode 100644
index 000000000..e5c7f8bdc
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/lrn_test.py
@@ -0,0 +1,66 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.caffe.extractors.lrn import lrn_ext
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.utils.unittest.extractors import FakeMultiParam
+
+
+class FakeProtoLayer:
+ def __init__(self, val):
+ self.lrn_param = val
+
+
+class TestLRN(unittest.TestCase):
+ def test_lrn_ext(self):
+ params = {
+ 'alpha': 10,
+ 'beta': 15,
+ 'local_size': 20,
+ 'norm_region': 0
+ }
+ res = lrn_ext(FakeProtoLayer(FakeMultiParam(params)), None)
+ exp_res = {
+ 'op': 'LRN',
+ 'type': 'Norm',
+ 'alpha': 10,
+ 'beta': 15,
+ 'local_size': 20,
+ 'region': 'across',
+ 'infer': copy_shape_infer
+ }
+ self.assertEqual(res, exp_res)
+
+ def test_lrn_ext_norm_reg(self):
+ params = {
+ 'alpha': 10,
+ 'beta': 15,
+ 'local_size': 20,
+ 'norm_region': 1
+ }
+ res = lrn_ext(FakeProtoLayer(FakeMultiParam(params)), None)
+ exp_res = {
+ 'op': 'LRN',
+ 'type': 'Norm',
+ 'alpha': 10,
+ 'beta': 15,
+ 'local_size': 20,
+ 'region': 'same',
+ 'infer': copy_shape_infer
+ }
+ self.assertEqual(res, exp_res)
diff --git a/model-optimizer/mo/front/caffe/extractors/permute_test.py b/model-optimizer/mo/front/caffe/extractors/permute_test.py
new file mode 100644
index 000000000..232e5200a
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/permute_test.py
@@ -0,0 +1,48 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.caffe.extractors.permute import permute_ext
+from mo.front.common.partial_infer.transpose import transpose_infer
+from mo.utils.unittest.extractors import FakeMultiParam
+
+
+class FakePermuteProtoLayer:
+ def __init__(self, val):
+ self.permute_param = val
+
+
+class TestPermuteParsing(unittest.TestCase):
+ def test_permute_check_attrs(self):
+ attrs = {
+ 'order': np.array([0, 1, 3, 2])
+ }
+
+ res = permute_ext(FakePermuteProtoLayer(FakeMultiParam(attrs)), None)
+ exp_attrs = {
+ 'type': 'Permute',
+ 'op': 'Permute',
+ 'order': np.array([0, 1, 3, 2]),
+ 'infer': transpose_infer
+ }
+ for key in exp_attrs.keys():
+ if key == 'order':
+ np.testing.assert_equal(res[key], exp_attrs[key])
+ else:
+ self.assertEqual(res[key], exp_attrs[key])
diff --git a/model-optimizer/mo/front/caffe/extractors/power_test.py b/model-optimizer/mo/front/caffe/extractors/power_test.py
new file mode 100644
index 000000000..5281bbb58
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/power_test.py
@@ -0,0 +1,46 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.caffe.extractors.power import power_ext
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.utils.unittest.extractors import FakeMultiParam
+
+
+class FakeProtoLayer:
+ def __init__(self, val):
+ self.power_param = val
+
+
+class TestPowerExt(unittest.TestCase):
+ def test_power_ext(self):
+ params = {
+ 'power': 1,
+ 'scale': 2,
+ 'shift': 3
+ }
+ res = power_ext(FakeProtoLayer(FakeMultiParam(params)), None)
+ exp_res = {
+ 'power': 1,
+ 'scale': 2,
+ 'shift': 3,
+ 'infer': copy_shape_infer,
+ 'op': "Power",
+ 'type': 'Power',
+ 'output_spatial_shape': None,
+ }
+ self.assertEqual(res, exp_res)
diff --git a/model-optimizer/mo/front/caffe/extractors/relu_test.py b/model-optimizer/mo/front/caffe/extractors/relu_test.py
new file mode 100644
index 000000000..b80716694
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/relu_test.py
@@ -0,0 +1,43 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.caffe.extractors.relu import relu_ext
+from mo.front.common.extractors.utils import layout_attrs
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.utils.unittest.extractors import FakeParam, FakeMultiParam
+
+
+class TestReLU(unittest.TestCase):
+ def test_relu_ext(self):
+ params = {
+ 'negative_slope': 0.1,
+ }
+
+ res = relu_ext(FakeParam('relu_param', FakeMultiParam(params)), None)
+ exp_res = {
+ 'negative_slope': 0.1,
+ 'infer': copy_shape_infer,
+ }
+ exp_res.update(layout_attrs())
+ for i in exp_res.keys():
+ if i == 'negative_slope':
+ self.assertEqual(res[i], exp_res[i])
+ else:
+ np.testing.assert_array_equal(res[i], exp_res[i])
diff --git a/model-optimizer/mo/front/caffe/extractors/reshape_test.py b/model-optimizer/mo/front/caffe/extractors/reshape_test.py
new file mode 100644
index 000000000..4551eb733
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/reshape_test.py
@@ -0,0 +1,56 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.caffe.extractors.reshape import reshape_ext
+from mo.utils.unittest.extractors import FakeMultiParam
+
+
+class FakeReshapeProtoLayer:
+ def __init__(self, val):
+ self.reshape_param = val
+
+
+class Shape:
+ def __init__(self, val):
+ self.dim = val
+
+
+class TestReshapeParsing(unittest.TestCase):
+ def test_reshape_check_attrs(self):
+ attrs = {
+ 'axis': 0,
+ 'num_axes': -1,
+ 'shape': Shape(np.array([0, -1])),
+ }
+
+ res = reshape_ext(FakeReshapeProtoLayer(FakeMultiParam(attrs)), None)
+ exp_attrs = {
+ 'op': 'Reshape',
+ 'type': 'Reshape',
+ 'axis': 0,
+ 'num_axes': -1,
+ 'dim': [0, -1]
+ }
+
+ for key in exp_attrs.keys():
+ if key == 'dim':
+ np.testing.assert_equal(res[key], exp_attrs[key])
+ else:
+ self.assertEqual(res[key], exp_attrs[key])
diff --git a/model-optimizer/mo/front/caffe/extractors/scale_test.py b/model-optimizer/mo/front/caffe/extractors/scale_test.py
new file mode 100644
index 000000000..9258295b8
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/scale_test.py
@@ -0,0 +1,144 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.caffe.extractors.scale import scale_ext
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.utils.unittest.extractors import FakeMultiParam, FakeModelLayer
+
+
+class FakeProtoLayer:
+ def __init__(self, val, bottom2=False):
+ self.scale_param = val
+ if bottom2:
+ self.bottom = {"bottom1", "bottom2"}
+ else:
+ self.bottom = {"bottom1"}
+
+
+class TestScale(unittest.TestCase):
+ def test_scale_ext(self):
+ mean_blob = np.array([1., 2.])
+ variance_blob = np.array([3., 4.])
+ blobs = [mean_blob, variance_blob]
+ params = {
+ 'type': 'Scale',
+ 'axis': 0,
+ 'bias_term': True
+ }
+
+ res = scale_ext(FakeProtoLayer(FakeMultiParam(params)), FakeModelLayer(blobs))
+ exp_res = {
+ 'op': 'ScaleShift',
+ 'type': 'ScaleShift',
+ 'axis': 0,
+ 'infer': copy_shape_infer,
+ 'weights': mean_blob,
+ 'biases': variance_blob,
+ 'embedded_inputs': [
+ (1, 'weights', {
+ 'bin': 'weights'
+ }),
+ (2, 'biases', {
+ 'bin': 'biases'
+ })
+ ]
+ }
+ for i in exp_res:
+ if i in ('weights', 'biases'):
+ np.testing.assert_array_equal(res[i], exp_res[i])
+ else:
+ self.assertEqual(res[i], exp_res[i])
+
+ def test_scale_2inputs_ext(self):
+ params = {
+ 'type': 'Scale',
+ 'axis': 0,
+ 'bias_term': False
+ }
+
+ res = scale_ext(FakeProtoLayer(FakeMultiParam(params), True), None)
+ exp_res = {
+ 'op': 'ScaleShift',
+ 'type': 'ScaleShift',
+ 'axis': 0,
+ 'infer': copy_shape_infer,
+ }
+ for i in exp_res:
+ self.assertEqual(res[i], exp_res[i])
+
+ def test_scale_2inputs_bias_ext(self):
+ variance_blob = np.array([3., 4.])
+ blobs = [variance_blob]
+
+ params = {
+ 'type': 'Scale',
+ 'axis': 0,
+ 'bias_term': True
+ }
+
+ res = scale_ext(FakeProtoLayer(FakeMultiParam(params), True), FakeModelLayer(blobs))
+ exp_res = {
+ 'op': 'ScaleShift',
+ 'type': 'ScaleShift',
+ 'axis': 0,
+ 'infer': copy_shape_infer,
+ 'biases': variance_blob,
+ 'embedded_inputs': [
+ (1, 'biases', {
+ 'bin': 'biases'
+ })]
+ }
+ for i in exp_res:
+ if i in ('biases'):
+ np.testing.assert_array_equal(res[i], exp_res[i])
+ else:
+ self.assertEqual(res[i], exp_res[i])
+
+ def test_create_default_weights(self):
+ """
+ There are situations when scale layer doesn't have weights and biases. This test checks that if they are not
+ available in the caffemodel file then default values [1] and [0] are generated.
+ """
+ scale_blob = np.array([1])
+ bias_blob = np.array([0])
+ params = {
+ 'type': 'Scale',
+ 'axis': 0,
+ 'bias_term': True
+ }
+
+ res = scale_ext(FakeProtoLayer(FakeMultiParam(params)), None)
+ exp_res = {
+ 'op': 'ScaleShift',
+ 'type': 'ScaleShift',
+ 'axis': 0,
+ 'infer': copy_shape_infer,
+ 'weights': scale_blob,
+ 'biases': bias_blob,
+ 'embedded_inputs': [
+ (1, 'weights', {
+ 'bin': 'weights'
+ }),
+ (2, 'biases', {
+ 'bin': 'biases'
+ })
+ ]
+ }
+ self.assertDictEqual(exp_res, res)
diff --git a/model-optimizer/mo/front/caffe/extractors/slice_test.py b/model-optimizer/mo/front/caffe/extractors/slice_test.py
new file mode 100644
index 000000000..22b43b81e
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/slice_test.py
@@ -0,0 +1,102 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+import numpy as np
+
+from mo.front.caffe.extractors.slice import slice_ext
+from mo.front.common.partial_infer.slice import caffe_slice_infer
+from mo.utils.unittest.extractors import FakeMultiParam
+
+
+class FakeProtoLayer:
+ def __init__(self, val):
+ self.slice_param = val
+
+
+class TestSlice(unittest.TestCase):
+ @patch('mo.front.caffe.extractors.slice.merge_attrs')
+ def test_slice_ext(self, merge_attrs_mock):
+ params = {
+ 'type': 'Slice',
+ 'axis': 2,
+ 'slice_point': np.array([256]),
+ 'slice_dim': 3,
+ 'infer': caffe_slice_infer
+ }
+ merge_attrs_mock.return_value = {
+ **params,
+ 'test': 54,
+ 'test2': 'test3'
+ }
+ res = slice_ext(FakeProtoLayer(FakeMultiParam(params)), None)
+ exp_res = {
+ 'type': 'Slice',
+ 'axis': 2,
+ 'slice_point': np.array([256]),
+ 'infer': caffe_slice_infer
+ }
+ for i in exp_res:
+ self.assertEqual(res[i], exp_res[i])
+
+ @patch('mo.front.caffe.extractors.slice.merge_attrs')
+ def test_slice_ext_slice_dim(self, merge_attrs_mock):
+ params = {
+ 'type': 'Slice',
+ 'axis': 1,
+ 'slice_point': np.array([256]),
+ 'slice_dim': 3,
+ 'infer': caffe_slice_infer
+ }
+ merge_attrs_mock.return_value = {
+ **params,
+ 'axis': 3
+ }
+ res = slice_ext(FakeProtoLayer(FakeMultiParam(params)), None)
+ exp_res = {
+ 'type': 'Slice',
+ 'axis': 3,
+ 'slice_point': np.array([256]),
+ 'infer': caffe_slice_infer
+ }
+ for i in exp_res:
+ self.assertEqual(res[i], exp_res[i])
+
+ @patch('mo.front.caffe.extractors.slice.merge_attrs')
+ def test_slice_ext_no_params(self, merge_attrs_mock):
+ params = {
+ 'type': 'Slice',
+ 'axis': 1,
+ 'slice_dim': 1,
+ 'slice_point': [],
+ 'infer': caffe_slice_infer
+ }
+ merge_attrs_mock.return_value = {
+ 'type': 'Slice',
+ 'axis': 1,
+ 'infer': caffe_slice_infer
+ }
+ res = slice_ext(FakeProtoLayer(FakeMultiParam(params)), None)
+ exp_res = {
+ 'type': 'Slice',
+ 'axis': 1,
+ 'slice_point': [],
+ 'infer': caffe_slice_infer
+ }
+ for i in exp_res:
+ self.assertEqual(res[i], exp_res[i])
diff --git a/model-optimizer/mo/front/caffe/extractors/utils_test.py b/model-optimizer/mo/front/caffe/extractors/utils_test.py
new file mode 100644
index 000000000..6983a0fdd
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/extractors/utils_test.py
@@ -0,0 +1,93 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch, call
+
+import numpy as np
+
+from mo.front.caffe.extractors.utils import weights_biases, embed_input, get_canonical_axis_index
+from mo.utils.unittest.extractors import FakeModelLayer
+
+
+class TestWeightsBiases(unittest.TestCase):
+ def test_weights_biases_no_layer_no_bias(self):
+ res = weights_biases(False, None)
+ self.assertEqual(res, {})
+
+ @patch('mo.front.caffe.extractors.utils.embed_input')
+ def test_weights_biases_layer_no_bias(self, embed_input_mock):
+ weights_biases(False, FakeModelLayer([[1, 2], ]))
+ calls = [call({}, 1, 'weights', [1, 2])]
+ embed_input_mock.assert_has_calls(calls)
+
+ @patch('mo.front.caffe.extractors.utils.embed_input')
+ def test_weights_biases_layer_bias(self, embed_input_mock):
+ weights_biases(True, FakeModelLayer([[1, 2], [3, 4]]))
+ calls = [call({}, 1, 'weights', [1, 2]), call({}, 2, 'biases', [3, 4])]
+ embed_input_mock.assert_has_calls(calls)
+
+
+class TestEmbedInput(unittest.TestCase):
+ def test_embed_input_no_bin_name_no_bias(self):
+ attrs = {}
+ blob = np.array([1, 2])
+ name = 'weights'
+ embed_input(attrs, 1, name, blob, None)
+ exp_res = {
+ 'weights': blob,
+ 'embedded_inputs': [
+ (1, name, {'bin': name})
+ ]
+ }
+ for key in exp_res.keys():
+ if key == name:
+ np.testing.assert_equal(attrs[key], exp_res[key])
+ else:
+ self.assertEqual(attrs[key], exp_res[key])
+
+ def test_embed_input_w_bin_name(self):
+ attrs = {}
+ blob = np.array([1, 2])
+ name = 'weights'
+ embed_input(attrs, 1, name, blob, 'special_name')
+ exp_res = {
+ 'weights': blob,
+ 'embedded_inputs': [
+ (1, name, {'bin': 'special_name'})
+ ]
+ }
+ for key in exp_res.keys():
+ if key == name:
+ np.testing.assert_equal(attrs[key], exp_res[key])
+ else:
+ self.assertEqual(attrs[key], exp_res[key])
+
+
+class TestCanonicalAxisIndex(unittest.TestCase):
+ def test_negative_index(self):
+ shape = [1, 2, 3, 4]
+ inds = [-4, -3, -2, -1]
+ expected_inds = [0, 1, 2, 3]
+ for i in range(len(inds)):
+ assert get_canonical_axis_index(shape, inds[i]) == expected_inds[i]
+
+ def test_posirive_index(self):
+ shape = [1, 2, 3, 4]
+ inds = [0, 1, 2, 3]
+ expected_inds = [0, 1, 2, 3]
+ for i in range(len(inds)):
+ assert get_canonical_axis_index(shape, inds[i]) == expected_inds[i]
diff --git a/model-optimizer/mo/front/caffe/loader_test.py b/model-optimizer/mo/front/caffe/loader_test.py
new file mode 100644
index 000000000..b61f6d37c
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/loader_test.py
@@ -0,0 +1,160 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+from google.protobuf import text_format
+
+from mo.front.caffe.loader import caffe_pb_to_nx
+from mo.front.caffe.proto import caffe_pb2
+from mo.utils.error import Error
+
+proto_str_one_input = 'name: "network" ' \
+ 'layer { ' \
+ 'name: "Input0" ' \
+ 'type: "Input" ' \
+ 'top: "Input0" ' \
+ 'input_param { ' \
+ 'shape: { ' \
+ 'dim: 1 ' \
+ 'dim: 3 ' \
+ 'dim: 224 ' \
+ 'dim: 224 ' \
+ '} ' \
+ '} ' \
+ '}'
+
+proto_str_old_styled_multi_input = 'name: "network" ' \
+ 'input: "Input0" ' \
+ 'input_dim: 1 ' \
+ 'input_dim: 3 ' \
+ 'input_dim: 224 ' \
+ 'input_dim: 224 ' \
+ 'input: "data"' \
+ 'input_dim: 1 ' \
+ 'input_dim: 3 '
+
+proto_str_input = 'name: "network" ' \
+ 'input: "data" ' \
+ 'input_shape ' \
+ '{ ' \
+ 'dim: 1 ' \
+ 'dim: 3 ' \
+ 'dim: 224 ' \
+ 'dim: 224 ' \
+ '}'
+
+proto_str_multi_input = 'name: "network" ' \
+ 'input: "data" ' \
+ 'input_shape ' \
+ '{ ' \
+ 'dim: 1 ' \
+ 'dim: 3 ' \
+ 'dim: 224 ' \
+ 'dim: 224 ' \
+ '} ' \
+ 'input: "data1"' \
+ 'input_shape ' \
+ '{ ' \
+ 'dim: 1 ' \
+ 'dim: 3 ' \
+ '}'
+
+proto_str_old_styled_input = 'name: "network" ' \
+ 'input: "data" ' \
+ 'input_dim: 1 ' \
+ 'input_dim: 3 ' \
+ 'input_dim: 224 ' \
+ 'input_dim: 224 '
+
+layer_proto_str = 'layer { ' \
+ 'name: "conv1" ' \
+ 'type: "Convolution" ' \
+ 'bottom: "data" ' \
+ 'top: "conv1" ' \
+ '}'
+
+proto_same_name_layers = 'layer { ' \
+ 'name: "conv1" ' \
+ 'type: "Convolution" ' \
+ 'bottom: "data" ' \
+ 'top: "conv1" ' \
+ '}' \
+ 'layer { ' \
+ 'name: "conv1" ' \
+ 'type: "Convolution" ' \
+ 'bottom: "data1" ' \
+ 'top: "conv1_2" ' \
+ '}'
+
+class TestLoader(unittest.TestCase):
+ def test_caffe_pb_to_nx_one_input(self):
+ proto = caffe_pb2.NetParameter()
+ text_format.Merge(proto_str_one_input, proto)
+ graph, input_shapes = caffe_pb_to_nx(proto, None)
+ expected_input_shapes = {
+ 'Input0': np.array([1, 3, 224, 224])
+ }
+
+ for i in expected_input_shapes:
+ np.testing.assert_array_equal(input_shapes[i], expected_input_shapes[i])
+
+ def test_caffe_pb_to_nx_old_styled_multi_input(self):
+ proto = caffe_pb2.NetParameter()
+ text_format.Merge(proto_str_old_styled_multi_input + layer_proto_str, proto)
+ self.assertRaises(Error, caffe_pb_to_nx, proto, None)
+
+ def test_caffe_pb_to_nx_old_styled_input(self):
+ proto = caffe_pb2.NetParameter()
+ text_format.Merge(proto_str_old_styled_input + layer_proto_str, proto)
+ graph, input_shapes = caffe_pb_to_nx(proto, None)
+ expected_input_shapes = {
+ 'data': np.array([1, 3, 224, 224])
+ }
+
+ for i in expected_input_shapes:
+ np.testing.assert_array_equal(input_shapes[i], expected_input_shapes[i])
+
+ def test_caffe_pb_to_standart_input(self):
+ proto = caffe_pb2.NetParameter()
+ text_format.Merge(proto_str_input + layer_proto_str, proto)
+ graph, input_shapes = caffe_pb_to_nx(proto, None)
+ expected_input_shapes = {
+ 'data': np.array([1, 3, 224, 224])
+ }
+
+ for i in expected_input_shapes:
+ np.testing.assert_array_equal(input_shapes[i], expected_input_shapes[i])
+
+ def test_caffe_pb_to_multi_input(self):
+ proto = caffe_pb2.NetParameter()
+ text_format.Merge(proto_str_multi_input + layer_proto_str, proto)
+ graph, input_shapes = caffe_pb_to_nx(proto, None)
+ expected_input_shapes = {
+ 'data': np.array([1, 3, 224, 224]),
+ 'data1': np.array([1, 3])
+ }
+
+ for i in expected_input_shapes:
+ np.testing.assert_array_equal(input_shapes[i], expected_input_shapes[i])
+
+ def test_caffe_same_name_layer(self):
+ proto = caffe_pb2.NetParameter()
+ text_format.Merge(proto_str_multi_input + proto_same_name_layers, proto)
+ graph, input_shapes = caffe_pb_to_nx(proto, None)
+ # 6 nodes because: 2 inputs + 2 convolutions + 2 output nodes
+ np.testing.assert_equal(len(graph.nodes()), 6)
diff --git a/model-optimizer/mo/front/caffe/python_layer_extractor_test.py b/model-optimizer/mo/front/caffe/python_layer_extractor_test.py
new file mode 100644
index 000000000..35f676078
--- /dev/null
+++ b/model-optimizer/mo/front/caffe/python_layer_extractor_test.py
@@ -0,0 +1,61 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.caffe.python_layer_extractor import PythonFrontExtractorOp
+from mo.front.extractor import CaffePythonFrontExtractorOp
+from mo.graph.graph import Node
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import FakeNode
+
+
+class FakePythonProtoLayer:
+ def __init__(self, params: FakeMultiParam):
+ self.type = 'Python'
+ self.python_param = params
+
+
+class FakePythonExtractor:
+ @staticmethod
+ def extract(node: Node):
+ return True
+
+
+class TestPythonLayerExtractor(unittest.TestCase):
+ def test_python_extractor_for_op(self):
+ module = 'test_module'
+ layer = 'test_layer'
+ CaffePythonFrontExtractorOp.registered_ops['{}.{}'.format(module, layer)] = \
+ lambda node: CaffePythonFrontExtractorOp.parse_param_str(node.pb.python_param.param_str)
+ params = FakeMultiParam({
+ 'module': module,
+ 'layer': layer,
+ 'param_str': "'feat_stride': 16"
+ })
+ ext = PythonFrontExtractorOp.extract(FakeNode(FakePythonProtoLayer(params), None))
+ self.assertEqual({'feat_stride': 16}, ext)
+
+ def test_python_extractor_for_extractors(self):
+ module = 'test_module'
+ layer = 'test_layer'
+ CaffePythonFrontExtractorOp.registered_ops['{}.{}'.format(module, layer)] = FakePythonExtractor
+ params = FakeMultiParam({
+ 'module': module,
+ 'layer': layer,
+ 'param_str': "'feat_stride': 16"
+ })
+ self.assertTrue(PythonFrontExtractorOp.extract(FakeNode(FakePythonProtoLayer(params), None)))
diff --git a/model-optimizer/mo/front/common/layout_test.py b/model-optimizer/mo/front/common/layout_test.py
new file mode 100644
index 000000000..e3865e44a
--- /dev/null
+++ b/model-optimizer/mo/front/common/layout_test.py
@@ -0,0 +1,115 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.common.layout import get_batch_dim, get_width_dim, get_height_dim, get_features_dim, get_depth_dim, \
+ shape_for_layout
+from mo.utils.error import Error
+
+
+class TestLayoutFunctions(unittest.TestCase):
+ def test_get_batch_dim_NCHW(self):
+ self.assertEqual(get_batch_dim('NCHW', 4), 0)
+
+ def test_get_batch_dim_NHWC(self):
+ self.assertEqual(get_batch_dim('NHWC', 4), 0)
+
+ def test_get_batch_dim_NCDHW(self):
+ self.assertEqual(get_batch_dim('NCHW', 5), 0)
+
+ def test_get_batch_dim_NDHWC(self):
+ self.assertEqual(get_batch_dim('NHWC', 5), 0)
+
+ def test_get_features_dim_NCHW(self):
+ self.assertEqual(get_features_dim('NCHW', 4), 1)
+
+ def test_get_features_dim_NHWC(self):
+ self.assertEqual(get_features_dim('NHWC', 4), 3)
+
+ def test_get_features_dim_NCDHW(self):
+ self.assertEqual(get_features_dim('NCHW', 5), 1)
+
+ def test_get_features_dim_NDHWC(self):
+ self.assertEqual(get_features_dim('NHWC', 5), 4)
+
+ def test_get_width_dim_NCHW(self):
+ self.assertEqual(get_width_dim('NCHW', 4), 3)
+
+ def test_get_width_dim_NHWC(self):
+ self.assertEqual(get_width_dim('NHWC', 4), 2)
+
+ def test_get_width_dim_NCDHW(self):
+ self.assertEqual(get_width_dim('NCHW', 5), 4)
+
+ def test_get_width_dim_NDHWC(self):
+ self.assertEqual(get_width_dim('NHWC', 5), 3)
+
+ def test_get_height_dim_NCHW(self):
+ self.assertEqual(get_height_dim('NCHW', 4), 2)
+
+ def test_get_height_dim_NHWC(self):
+ self.assertEqual(get_height_dim('NHWC', 4), 1)
+
+ def test_get_height_dim_NCDHW(self):
+ self.assertEqual(get_height_dim('NCHW', 5), 3)
+
+ def test_get_height_dim_NDHWC(self):
+ self.assertEqual(get_height_dim('NHWC', 5), 2)
+
+ def test_get_depth_dim_NCDHW(self):
+ self.assertEqual(get_depth_dim('NCHW', 5), 2)
+
+ def test_get_depth_dim_NDHWC(self):
+ self.assertEqual(get_depth_dim('NHWC', 5), 1)
+
+ def test_get_batch_dim_wrong_layout(self):
+ self.assertRaises(AssertionError, get_batch_dim, 'NCDHW', 5)
+
+ def test_get_width_dim_wrong_layout(self):
+ self.assertRaises(AssertionError, get_width_dim, 'NCDHW', 5)
+
+ def test_get_height_dim_wrong_layout(self):
+ self.assertRaises(AssertionError, get_height_dim, 'NCDHW', 5)
+
+ def test_get_features_dim_wrong_layout(self):
+ self.assertRaises(AssertionError, get_features_dim, 'NCDHW', 5)
+
+ def test_shape_for_layout_NCHW(self):
+ self.assertListEqual([2, 3, 4, 5], list(shape_for_layout('NCHW', batch=2, features=3, height=4, width=5)))
+
+ def test_shape_for_layout_NHWC(self):
+ self.assertListEqual([2, 4, 5, 3], list(shape_for_layout('NHWC', batch=2, features=3, height=4, width=5)))
+
+ def test_shape_for_layout_missing_batch(self):
+ with self.assertRaises(Error):
+ shape_for_layout('NCHW', features=3, height=4, width=5)
+
+ def test_shape_for_layout_missing_features(self):
+ with self.assertRaises(Error):
+ shape_for_layout('NCHW', batch=2, height=4, width=5)
+
+ def test_shape_for_layout_missing_height(self):
+ with self.assertRaises(Error):
+ shape_for_layout('NHWC', batch=2, features=3, width=5)
+
+ def test_shape_for_layout_missing_width(self):
+ with self.assertRaises(Error):
+ shape_for_layout('NHWC', batch=2, features=3, height=4)
+
+ def test_shape_for_layout_unknown_parameter(self):
+ with self.assertRaises(Error):
+ shape_for_layout('NHWC', batch=2, features=3, height=4, width=5, unknown_parameter=123)
diff --git a/model-optimizer/mo/front/common/partial_infer/caffe_fallback_test.py b/model-optimizer/mo/front/common/partial_infer/caffe_fallback_test.py
new file mode 100644
index 000000000..1d03857dc
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/caffe_fallback_test.py
@@ -0,0 +1,109 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import MagicMock
+
+import numpy as np
+
+from mo.front.common.partial_infer.caffe_fallback import build_net
+from mo.utils.unittest.extractors import FakeMultiParam, FakeValue
+from mo.utils.unittest.graph import build_graph
+
+
+class Net:
+ def __init__(self, blobs):
+ self.blobs = blobs
+ self.reshape_blob = MagicMock(return_value=np.array([1, 1, 1, 1]))
+ self.reshape = MagicMock(return_value=np.array([1, 1, 1, 1]))
+ self.forward = MagicMock(return_value={'top_node': FakeValue(np.array([1, 3, 112, 112]))})
+
+
+my_mock_net = None
+
+
+class Caffe:
+ def __init__(self):
+ self.TEST = 'TEST'
+
+ def Net(self, *args):
+ return my_mock_net
+
+
+class TestCaffeNativePartialInfer(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ import sys
+ sys.modules['caffe'] = Caffe()
+ cls.nodes_attributes = {
+ 'node_1': {'type': 'Input', 'kind': 'op'},
+ 'node_2': {'type': 'Input', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'kind': 'op'},
+ 'node_4': {'type': 'Identity', 'kind': 'op'}
+ }
+
+ def test_build_net_equal_inputs(self):
+ global my_mock_net
+ my_blobs = {
+ 'node_1': FakeValue(np.array([1, 3, 227, 227])),
+ 'node_2': FakeValue(np.array([1, 3, 224, 224]))
+ }
+ my_mock_net = Net(my_blobs)
+ graph = build_graph(self.nodes_attributes,
+ [
+ ('node_1', 'node_3'),
+ ('node_2', 'node_3'),
+ ('node_3', 'node_4')
+ ],
+ {
+ 'node_4': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 224, 224])},
+ 'node_3': {'top': 'top_node'}
+ })
+ graph.proto_path = 'path_to_proto'
+ graph.caffemodel_path = 'path_to_proto'
+ build_net(graph)
+ my_mock_net.reshape.assert_not_called()
+ my_mock_net.forward.assert_called_once_with()
+ self.assertIsNotNone(graph.caffe_net)
+
+ def test_build_net_not_equal_inputs(self):
+ global my_mock_net
+ input_node_param = {
+ 'shape': np.array([1, 3, 112, 112]),
+ 'reshape': MagicMock(return_value=134)
+ }
+ my_blobs = {
+ 'node_1': FakeMultiParam(input_node_param),
+ }
+ my_mock_net = Net(my_blobs)
+ graph = build_graph(self.nodes_attributes,
+ [
+ ('node_1', 'node_3'),
+ ('node_3', 'node_4')
+ ],
+ {'node_4': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_3': {'top': 'top_node'}
+ },
+ nodes_with_edges_only=True)
+ graph.proto_path = 'path_to_proto'
+ graph.caffemodel_path = 'path_to_proto'
+ build_net(graph)
+ my_mock_net.reshape.assert_called_once_with()
+ my_mock_net.forward.assert_called_once_with()
+ self.assertIsNotNone(graph.caffe_net)
diff --git a/model-optimizer/mo/front/common/partial_infer/concat_test.py b/model-optimizer/mo/front/common/partial_infer/concat_test.py
new file mode 100644
index 000000000..07b53a1c8
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/concat_test.py
@@ -0,0 +1,99 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.concat import concat_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'kind': 'data', 'value': None},
+ 'node_2': {'kind': 'data', 'value': None},
+ 'concat': {'type': 'Concat', 'kind': 'op'},
+ 'node_3': {'kind': 'data'}
+ }
+
+
+class TestConcatPartialInfer(unittest.TestCase):
+ def test_tf_concat_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'concat'),
+ ('node_2', 'concat'),
+ ('concat', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 227, 227])},
+ 'concat': {'axis': 2}
+ })
+
+ concat_node = Node(graph, 'concat')
+ concat_infer(concat_node)
+ exp_shape = np.array([1, 3, 454, 227])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_tf_concat_infer_negative_axis(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'concat'),
+ ('node_2', 'concat'),
+ ('concat', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 227, 227])},
+ 'concat': {'axis': -1}
+ })
+
+ concat_node = Node(graph, 'concat')
+ concat_infer(concat_node)
+ exp_shape = np.array([1, 3, 227, 454])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_tf_concat_infer_not_match(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'concat'),
+ ('node_2', 'concat'),
+ ('concat', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 2, 227, 227])},
+ 'concat': {'axis': 2}
+ })
+
+ concat_node = Node(graph, 'concat')
+ concat_infer(concat_node)
+ res_shape = graph.node['node_3']['shape']
+ self.assertIsNone(res_shape)
+
+ def test_tf_concat_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'concat'),
+ ('node_2', 'concat'),
+ ('concat', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': None},
+ 'concat': {'axis': 2}
+ })
+
+ concat_node = Node(graph, 'concat')
+ concat_infer(concat_node)
+ res_shape = graph.node['node_3']['shape']
+ self.assertIsNone(res_shape)
diff --git a/model-optimizer/mo/front/common/partial_infer/crop_test.py b/model-optimizer/mo/front/common/partial_infer/crop_test.py
new file mode 100644
index 000000000..d1eb97b4d
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/crop_test.py
@@ -0,0 +1,123 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.crop import crop_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'value': None, 'kind': 'data'},
+ 'node_2': {'value': None, 'kind': 'data'},
+ 'crop_1': {'type': 'Crop', 'kind': 'op'},
+ 'node_3': {'value': None, 'kind': 'data'}
+ }
+
+
+class TestCropInfer(unittest.TestCase):
+ def test_crop_infer_ideal(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'crop_1'),
+ ('node_2', 'crop_1'),
+ ('crop_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 2, 500, 500])},
+ 'node_2': {'shape': np.array([1, 2, 256, 256])},
+ 'crop_1': {'axis': 2, 'offset': [0, 0], 'dim': None}
+ })
+
+ crop_node = Node(graph, 'crop_1')
+
+ crop_infer(crop_node)
+ exp_shape = np.array([1, 2, 256, 256])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ self.assertEqual(crop_node.axis, [2, 3])
+ self.assertEqual(crop_node.offset, [0, 0])
+ self.assertEqual(crop_node.dim, [256, 256])
+
+ def test_crop_infer_negative_axis(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'crop_1'),
+ ('node_2', 'crop_1'),
+ ('crop_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 2, 500, 500])},
+ 'node_2': {'shape': np.array([1, 2, 256, 256])},
+ 'crop_1': {'axis': -1, 'offset': [0, 0], 'dim': None}
+ })
+
+ crop_node = Node(graph, 'crop_1')
+
+ crop_infer(crop_node)
+ exp_shape = np.array([1, 2, 500, 256])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ self.assertEqual(crop_node.axis, [3])
+ self.assertEqual(crop_node.offset, [0])
+ self.assertEqual(crop_node.dim, [256])
+
+ def test_crop_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'crop_1'),
+ ('node_2', 'crop_1'),
+ ('crop_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 2, 500, 500])},
+ 'node_2': {'shape': None},
+ 'crop_1': {'axis': 2, 'offset': [0, 0], 'dim': None}
+ })
+
+ crop_node = Node(graph, 'crop_1')
+
+ crop_infer(crop_node)
+ self.assertIsNone(graph.node['node_3']['shape'])
+
+ def test_crop_infer_one_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'crop_1'),
+ ('crop_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 2, 500, 500])},
+ 'crop_1': {'axis': 2, 'offset': [0], 'dim': None}
+ })
+
+ crop_node = Node(graph, 'crop_1')
+
+ crop_infer(crop_node)
+ self.assertIsNone(graph.node['node_3']['shape'])
+
+ def test_crop_infer_out_offset(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'crop_1'),
+ ('node_2', 'crop_1'),
+ ('crop_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 2, 500, 500])},
+ 'node_2': {'shape': np.array([1, 2, 256, 256])},
+ 'crop_1': {'axis': 2, 'offset': [300], 'dim': None}
+ })
+
+ crop_node = Node(graph, 'crop_1')
+
+ crop_infer(crop_node)
+ self.assertIsNone(graph.node['node_3']['shape'])
diff --git a/model-optimizer/mo/front/common/partial_infer/elemental_test.py b/model-optimizer/mo/front/common/partial_infer/elemental_test.py
new file mode 100644
index 000000000..78d1daeab
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/elemental_test.py
@@ -0,0 +1,39 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+import numpy as np
+
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+
+
+class FakeNode:
+ def __init__(self, blob):
+ self.blob = blob
+
+ def in_shape(self):
+ return self.blob
+
+
+class TestElementalInference(unittest.TestCase):
+ @patch('mo.front.common.partial_infer.elemental.single_output_infer')
+ def test_copy_shape_infer(self, single_output_infer_mock):
+ single_output_infer_mock.return_value = 0
+ node = FakeNode(np.array([1, 2]))
+ copy_shape_infer(node)
+ self.assertTrue(single_output_infer_mock.called)
diff --git a/model-optimizer/mo/front/common/partial_infer/eltwise_test.py b/model-optimizer/mo/front/common/partial_infer/eltwise_test.py
new file mode 100644
index 000000000..5b57bf6e7
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/eltwise_test.py
@@ -0,0 +1,139 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.eltwise import eltwise_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'value': 2, 'kind': 'data'},
+ 'node_2': {'value': 3, 'kind': 'data'},
+ 'eltw_1': {'type': 'Eltwise', 'kind': 'op'},
+ 'node_3': {'value': None, 'kind': 'data'}
+ }
+
+
+class TestEltwiseInfer(unittest.TestCase):
+ def test_eltwise_infer_max(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'eltw_1'),
+ ('node_2', 'eltw_1'),
+ ('eltw_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 256, 256])},
+ 'node_2': {'shape': np.array([1, 3, 256, 256])},
+ 'eltw_1': {}
+ })
+
+ graph.graph['layout'] = 'NCHW'
+
+ eltwise_node = Node(graph, 'eltw_1')
+
+ eltwise_infer(eltwise_node, lambda a, b: np.maximum(a, b))
+ exp_shape = np.array([1, 3, 256, 256])
+ exp_value = 3
+ res_shape = graph.node['node_3']['shape']
+ res_value = eltwise_node.out_node().value
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ self.assertEqual(exp_value, res_value)
+
+ def test_eltwise_infer_sum(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'eltw_1'),
+ ('node_2', 'eltw_1'),
+ ('eltw_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 256, 256])},
+ 'node_2': {'shape': np.array([1, 3, 256, 256])}
+ })
+ graph.graph['layout'] = 'NCHW'
+ eltwise_node = Node(graph, 'eltw_1')
+
+ eltwise_infer(eltwise_node, lambda a, b: a + b)
+ exp_shape = np.array([1, 3, 256, 256])
+ exp_value = 5
+ res_shape = graph.node['node_3']['shape']
+ res_value = eltwise_node.out_node().value
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ self.assertEqual(exp_value, res_value)
+
+ def test_eltwise_infer_mul(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'eltw_1'),
+ ('node_2', 'eltw_1'),
+ ('eltw_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 256, 256])},
+ 'node_2': {'shape': np.array([1, 3, 256, 256])}
+ })
+ graph.graph['layout'] = 'NCHW'
+ eltwise_node = Node(graph, 'eltw_1')
+
+ eltwise_infer(eltwise_node, lambda a, b: a * b)
+ exp_shape = np.array([1, 3, 256, 256])
+ exp_value = 6
+ res_shape = graph.node['node_3']['shape']
+ res_value = eltwise_node.out_node().value
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ self.assertEqual(exp_value, res_value)
+
+ def test_eltwise_infer_none_val(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'eltw_1'),
+ ('node_2', 'eltw_1'),
+ ('eltw_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 256, 256]), 'value': None},
+ 'node_2': {'shape': np.array([1, 3, 256, 256])}
+ })
+ graph.graph['layout'] = 'NCHW'
+ eltwise_node = Node(graph, 'eltw_1')
+
+ eltwise_infer(eltwise_node, lambda a, b: a * b)
+ exp_shape = np.array([1, 3, 256, 256])
+ res_shape = graph.node['node_3']['shape']
+ res_value = eltwise_node.out_node().value
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ self.assertIsNone(res_value)
+
+ def test_eltwise_infer_none_min_max(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'eltw_1'),
+ ('node_2', 'eltw_1'),
+ ('eltw_1', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 257, 256])},
+ 'node_2': {'shape': np.array([1, 3, 256, 257])}
+ })
+ graph.graph['layout'] = 'NCHW'
+ eltwise_node = Node(graph, 'eltw_1')
+
+ eltwise_infer(eltwise_node)
+ exp_shape = np.array([1, 3, -1, -1])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/mo/front/common/partial_infer/expand_dims_test.py b/model-optimizer/mo/front/common/partial_infer/expand_dims_test.py
new file mode 100644
index 000000000..69dbc44c6
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/expand_dims_test.py
@@ -0,0 +1,160 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.expand_dims import tf_expand_dims_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'input_1': {'kind': 'data', 'value': None},
+ 'input_2': {'kind': 'data', 'value': None},
+ 'expand_dims': {'kind': 'op'},
+ 'out': {'value': None, 'shape': None, 'kind': 'data'}
+ }
+
+
+class TestExpandDimsInfer(unittest.TestCase):
+ def test_expand_dims_infer_two_inputs(self):
+ graph = build_graph(nodes_attributes,
+ [('input_1', 'expand_dims'),
+ ('input_2', 'expand_dims'),
+ ('expand_dims', 'out')],
+ {'input_1': {'shape': np.array([3, 256, 256])},
+ 'input_2': {'shape': np.array([1]), 'value': np.array([1], dtype=np.int32)},
+ })
+
+ expand_dims_node = Node(graph, 'expand_dims')
+
+ tf_expand_dims_infer(expand_dims_node)
+ exp_shape = np.array([3, 1, 256, 256])
+ res_shape = expand_dims_node.out_node().shape
+ self.assertEqual(len(exp_shape), len(res_shape))
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_expand_dims_infer_two_inputs_2(self):
+ graph = build_graph(nodes_attributes,
+ [('input_1', 'expand_dims'),
+ ('input_2', 'expand_dims'),
+ ('expand_dims', 'out')],
+ {'input_1': {'shape': np.array([3, 256, 256])},
+ 'input_2': {'shape': np.array([1]), 'value': np.array([2], dtype=np.int32)},
+ })
+
+ expand_dims_node = Node(graph, 'expand_dims')
+
+ tf_expand_dims_infer(expand_dims_node)
+ exp_shape = np.array([3, 256, 1, 256])
+ res_shape = expand_dims_node.out_node().shape
+ self.assertEqual(len(exp_shape), len(res_shape))
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_expand_dims_infer_two_inputs_3(self):
+ graph = build_graph(nodes_attributes,
+ [('input_1', 'expand_dims'),
+ ('input_2', 'expand_dims'),
+ ('expand_dims', 'out')],
+ {'input_1': {'shape': np.array([3, 256, 256])},
+ 'input_2': {'shape': np.array([]), 'value': np.array(3, dtype=np.int32)},
+ })
+
+ expand_dims_node = Node(graph, 'expand_dims')
+
+ tf_expand_dims_infer(expand_dims_node)
+ exp_shape = np.array([3, 256, 256, 1])
+ res_shape = expand_dims_node.out_node().shape
+ self.assertEqual(len(exp_shape), len(res_shape))
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_expand_dims_infer_two_inputs_negative(self):
+ graph = build_graph(nodes_attributes,
+ [('input_1', 'expand_dims'),
+ ('input_2', 'expand_dims'),
+ ('expand_dims', 'out')],
+ {'input_1': {'shape': np.array([3, 256, 256])},
+ 'input_2': {'shape': np.array([1]), 'value': np.array([2, 3], dtype=np.int32)},
+ })
+
+ expand_dims_node = Node(graph, 'expand_dims')
+
+ tf_expand_dims_infer(expand_dims_node)
+ self.assertIsNone(expand_dims_node.out_node().shape)
+
+ def test_expand_dims_infer_two_inputs_negative_2(self):
+ graph = build_graph(nodes_attributes,
+ [('input_1', 'expand_dims'),
+ ('input_2', 'expand_dims'),
+ ('expand_dims', 'out')],
+ {'input_1': {'shape': None},
+ 'input_2': {'shape': np.array([1]), 'value': np.array([2, 3], dtype=np.int32)},
+ })
+
+ expand_dims_node = Node(graph, 'expand_dims')
+
+ tf_expand_dims_infer(expand_dims_node)
+ self.assertIsNone(expand_dims_node.out_node().shape)
+
+ def test_expand_dims_infer_one_input(self):
+ graph = build_graph(nodes_attributes,
+ [('input_1', 'expand_dims'),
+ ('expand_dims', 'out')],
+ {'input_1': {'shape': np.array([3, 256, 256])},
+ 'expand_dims': {'expand_axis': 1}
+ })
+
+ expand_dims_node = Node(graph, 'expand_dims')
+
+ tf_expand_dims_infer(expand_dims_node)
+ exp_shape = np.array([3, 1, 256, 256])
+ res_shape = expand_dims_node.out_node().shape
+ self.assertEqual(len(exp_shape), len(res_shape))
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_expand_dims_infer_one_input_2(self):
+ graph = build_graph(nodes_attributes,
+ [('input_1', 'expand_dims'),
+ ('expand_dims', 'out')],
+ {'input_1': {'shape': np.array([3, 256, 256])},
+ 'expand_dims': {'expand_axis': 2}
+ })
+
+ expand_dims_node = Node(graph, 'expand_dims')
+
+ tf_expand_dims_infer(expand_dims_node)
+ exp_shape = np.array([3, 256, 1, 256])
+ res_shape = expand_dims_node.out_node().shape
+ self.assertEqual(len(exp_shape), len(res_shape))
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_expand_dims_infer_one_input_negative(self):
+ graph = build_graph(nodes_attributes,
+ [('input_1', 'expand_dims'),
+ ('expand_dims', 'out')],
+ {'input_1': {'shape': np.array([3, 256, 256])},
+ 'expand_dims': {'expand_axis': None}
+ })
+
+ expand_dims_node = Node(graph, 'expand_dims')
+
+ tf_expand_dims_infer(expand_dims_node)
+ self.assertIsNone(expand_dims_node.out_node().shape)
diff --git a/model-optimizer/mo/front/common/partial_infer/inner_product_test.py b/model-optimizer/mo/front/common/partial_infer/inner_product_test.py
new file mode 100644
index 000000000..8b3931275
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/inner_product_test.py
@@ -0,0 +1,74 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.inner_product import caffe_inner_product
+from mo.graph.graph import Node
+from mo.utils.unittest.extractors import FakeValue
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'value': None, 'kind': 'data'},
+ 'inner': {'type': 'FullyConnected', 'value': None, 'kind': 'op'},
+ 'node_2': {'value': FakeValue(None), 'kind': 'data'},
+ 'node_3': {'value': None, 'kind': 'data'}
+ }
+
+
+class TestInnerPartialInfer(unittest.TestCase):
+ def test_inner_product_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'inner'),
+ ('node_2', 'inner'),
+ ('inner', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 256, 256])},
+ 'node_2': {'shape': np.array([1, 3, 256, 256]),
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'inner': {'out-size': 4}
+ })
+
+ inner_node = Node(graph, 'inner')
+
+ caffe_inner_product(inner_node)
+ exp_shape = np.array([1, 4])
+ exp_shape_in_node = np.array([4, 3 * 256 * 256])
+ res_shape = graph.node['node_3']['shape']
+ res_shape_in_node = inner_node.in_node(1).shape
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ for i in range(0, len(exp_shape_in_node)):
+ self.assertEqual(exp_shape_in_node[i], res_shape_in_node[i])
+
+ def test_inner_product_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'inner'),
+ ('node_2', 'inner'),
+ ('inner', 'node_3')],
+ {'node_3': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': None},
+ 'node_2': {'shape': np.array([1, 3, 256, 256])},
+ 'inner': {'out-size': 4}
+ })
+
+ inner_node = Node(graph, 'inner')
+
+ caffe_inner_product(inner_node)
+ res_shape = graph.node['node_3']['shape']
+ self.assertIsNone(res_shape)
diff --git a/model-optimizer/mo/front/common/partial_infer/multi_box_detection_test.py b/model-optimizer/mo/front/common/partial_infer/multi_box_detection_test.py
new file mode 100644
index 000000000..ad9859f5e
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/multi_box_detection_test.py
@@ -0,0 +1,129 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.multi_box_detection import multi_box_detection_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'value': None, 'kind': 'data'},
+ 'node_2': {'value': None, 'kind': 'data'},
+ 'node_3': {'value': None, 'kind': 'data'},
+ 'detection_output_1': {'type': 'DetectionOutput', 'value': None, 'kind': 'op'},
+ 'node_4': {'value': None, 'kind': 'data'}
+ }
+
+
+class TestMultiBoxDetectionInfer(unittest.TestCase):
+ def test_prior_box_infer_ideal(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'detection_output_1'),
+ ('node_2', 'detection_output_1'),
+ ('node_3', 'detection_output_1'),
+ ('detection_output_1', 'node_4')],
+ {'node_1': {'shape': np.array([1, 34928])},
+ 'node_2': {'shape': np.array([1, 183372])},
+ 'node_3': {'shape': np.array([1, 2, 34928])},
+ 'detection_output_1': {"background_label_id": "0", "clip": "1",
+ "code_type": "caffe.PriorBoxParameter.CENTER_SIZE",
+ "confidence_threshold": "0.01", "keep_top_k": "200",
+ "nms_threshold": "0.5", "num_classes": "21",
+ "share_location": "1", "top_k": "200",
+ "variance_encoded_in_target": "0"},
+ 'node_4': {'shape': np.array([1, 1, 200, 7])},
+ })
+
+ multi_box_detection_node = Node(graph, 'detection_output_1')
+ print(multi_box_detection_node)
+
+ multi_box_detection_infer(multi_box_detection_node)
+ exp_shape = np.array([1, 1, 200, 7])
+ res_shape = graph.node['node_4']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ self.assertEqual(multi_box_detection_node.background_label_id, '0')
+ self.assertEqual(multi_box_detection_node.clip, '1')
+ self.assertEqual(multi_box_detection_node.code_type, 'caffe.PriorBoxParameter.CENTER_SIZE')
+ self.assertEqual(multi_box_detection_node.confidence_threshold, '0.01')
+ self.assertEqual(multi_box_detection_node.keep_top_k, '200')
+ self.assertEqual(multi_box_detection_node.nms_threshold, '0.5')
+ self.assertEqual(multi_box_detection_node.num_classes, 21)
+ self.assertEqual(multi_box_detection_node.share_location, '1')
+ self.assertEqual(multi_box_detection_node.top_k, '200')
+ self.assertEqual(multi_box_detection_node.variance_encoded_in_target, '0')
+
+ def test_prior_box_infer_without_top_k(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'detection_output_1'),
+ ('node_2', 'detection_output_1'),
+ ('node_3', 'detection_output_1'),
+ ('detection_output_1', 'node_4')],
+ {'node_1': {'shape': np.array([1, 34928])},
+ 'node_2': {'shape': np.array([1, 183372])},
+ 'node_3': {'shape': np.array([1, 2, 34928])},
+ 'detection_output_1': {"background_label_id": "0", "clip": "1",
+ "code_type": "caffe.PriorBoxParameter.CENTER_SIZE",
+ "confidence_threshold": "0.01", "keep_top_k": -1,
+ "nms_threshold": "0.5", "num_classes": "21",
+ "share_location": "1", "top_k": -1,
+ "variance_encoded_in_target": "0"},
+ 'node_4': {'shape': np.array([1, 1, 69856, 7])},
+ })
+
+ multi_box_detection_node = Node(graph, 'detection_output_1')
+
+ multi_box_detection_infer(multi_box_detection_node)
+ exp_shape = np.array([1, 1, 8732, 7])
+ res_shape = graph.node['node_4']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ self.assertEqual(multi_box_detection_node.background_label_id, '0')
+ self.assertEqual(multi_box_detection_node.clip, '1')
+ self.assertEqual(multi_box_detection_node.code_type, 'caffe.PriorBoxParameter.CENTER_SIZE')
+ self.assertEqual(multi_box_detection_node.confidence_threshold, '0.01')
+ self.assertEqual(multi_box_detection_node.keep_top_k, 8732)
+ self.assertEqual(multi_box_detection_node.nms_threshold, '0.5')
+ self.assertEqual(multi_box_detection_node.num_classes, 21)
+ self.assertEqual(multi_box_detection_node.share_location, '1')
+ self.assertEqual(multi_box_detection_node.top_k, -1)
+ self.assertEqual(multi_box_detection_node.variance_encoded_in_target, '0')
+
+ def test_prior_box_infer_raise_error(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'detection_output_1'),
+ ('node_2', 'detection_output_1'),
+ ('node_3', 'detection_output_1'),
+ ('detection_output_1', 'node_4')],
+ {'node_1': {'shape': np.array([1, 34928])},
+ 'node_2': {'shape': np.array([1, 183372])},
+ 'node_3': {'shape': np.array([1, 3, 34928])},
+ 'detection_output_1': {"background_label_id": "0", "clip": "1",
+ "code_type": "caffe.PriorBoxParameter.CENTER_SIZE",
+ "confidence_threshold": "0.01", "keep_top_k": -1,
+ "nms_threshold": "0.5", "num_classes": "21",
+ "share_location": "1", "top_k": -1,
+ "variance_encoded_in_target": 0},
+ 'node_4': {'shape': np.array([1, 1, 69856, 7])},
+ })
+
+ multi_box_detection_node = Node(graph, 'detection_output_1')
+
+ self.assertIsNone(multi_box_detection_infer(multi_box_detection_node))
diff --git a/model-optimizer/mo/front/common/partial_infer/multi_box_prior_test.py b/model-optimizer/mo/front/common/partial_infer/multi_box_prior_test.py
new file mode 100644
index 000000000..6e1ce7c88
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/multi_box_prior_test.py
@@ -0,0 +1,58 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.multi_box_prior import multi_box_prior_infer_mxnet
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'value': None, 'kind': 'data'},
+ 'node_2': {'value': None, 'kind': 'data'},
+ 'prior_box_1': {'type': 'PriorBox', 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'data'}
+ }
+
+
+class TestMultiBoxPriorInfer(unittest.TestCase):
+ def test_prior_box_infer_ideal(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'prior_box_1'),
+ ('node_2', 'prior_box_1'),
+ ('prior_box_1', 'node_3')],
+ {'node_1': {'shape': np.array([1, 1024, 19, 19])},
+ 'node_2': {'shape': np.array([1, 3, 300, 300])},
+ 'prior_box_1': {'aspect_ratio': [1.0, 2.0, 0.5, 3.0, 0.333333333333],
+ 'min_size': [0.2, 0.272],
+ 'max_size': '', 'offset': 0.5, 'step': 0.2, 'sizes': [0.2, 0.272]},
+ 'node_3': {'shape': np.array([1, 2, 3])},
+ })
+
+ multi_box_prior_node = Node(graph, 'prior_box_1')
+
+ multi_box_prior_infer_mxnet(multi_box_prior_node)
+ exp_shape = np.array([1, 2, 8664])
+ res_shape = graph.node['node_3']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ self.assertEqual(multi_box_prior_node.min_size, [60.0, 81.600000000000009])
+ self.assertEqual(multi_box_prior_node.max_size, '')
+ self.assertEqual(multi_box_prior_node.aspect_ratio, [1.0, 2.0, 0.5, 3.0, 0.333333333333])
+ self.assertEqual(round(multi_box_prior_node.step, 1), 60.0)
+ self.assertEqual(round(multi_box_prior_node.offset, 1), 0.5)
diff --git a/model-optimizer/mo/front/common/partial_infer/range_test.py b/model-optimizer/mo/front/common/partial_infer/range_test.py
new file mode 100644
index 000000000..113c49b78
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/range_test.py
@@ -0,0 +1,114 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.range import tf_range_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.extractors import FakeParam
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'start': {'kind': 'data'},
+ 'limit': {'kind': 'data'},
+ 'delta': {'kind': 'data'},
+ 'range': {'kind': 'op'},
+ 'output': {'value': None, 'shape': None, 'kind': 'data'},
+ }
+edges = [('start', 'range'), ('limit', 'range'), ('delta', 'range'), ('range', 'output')]
+
+
+class TestRangePartialInfer(unittest.TestCase):
+ def test_int32_specific_data_type_range_infer(self):
+ # import tensorflow to use TF data types
+ import tensorflow as tf
+ graph = build_graph(nodes_attributes, edges,
+ {'start': {'value': np.array([1])},
+ 'limit': {'value': np.array([5])},
+ 'delta': {'value': np.array([1])},
+ 'range': {'pb': FakeParam('attr', dict(type=FakeParam('type', tf.int32)))},
+ })
+
+ range_node = Node(graph, 'range')
+
+ tf_range_infer(range_node)
+ exp_value = np.array([1, 2, 3, 4], dtype=np.int32)
+ out_value = graph.node['output']['value']
+
+ self.assertTrue(exp_value.dtype == out_value.dtype)
+ self.assertTrue(np.array_equal(exp_value.shape, out_value.shape))
+ self.assertTrue(np.array_equal(exp_value, out_value))
+
+ def test_automatic_data_type_range_infer(self):
+ graph = build_graph(nodes_attributes, edges,
+ {'start': {'value': np.array([2], dtype=np.float32)},
+ 'limit': {'value': np.array([5])},
+ 'delta': {'value': np.array([1])},
+ 'range': {'pb': FakeParam('attr', dict())},
+ })
+
+ range_node = Node(graph, 'range')
+
+ tf_range_infer(range_node)
+ exp_value = np.array([2.0, 3.0, 4.0], dtype=np.float32)
+ out_value = graph.node['output']['value']
+
+ self.assertTrue(exp_value.dtype == out_value.dtype)
+ self.assertTrue(np.array_equal(exp_value.shape, out_value.shape))
+ self.assertTrue(np.array_equal(exp_value, out_value))
+
+ def test_non_constant_start_range_infer(self):
+ graph = build_graph(nodes_attributes, edges,
+ {'start': {},
+ 'limit': {'value': np.array([5])},
+ 'delta': {'value': np.array([1])},
+ 'range': {'pb': FakeParam('attr', dict())},
+ })
+
+ range_node = Node(graph, 'range')
+
+ tf_range_infer(range_node)
+ out_value = graph.node['output']['value']
+ self.assertIsNone(out_value)
+
+ def test_non_constant_limit_range_infer(self):
+ graph = build_graph(nodes_attributes, edges,
+ {'start': {'value': np.array([1])},
+ 'limit': {},
+ 'delta': {'value': np.array([1])},
+ 'range': {'pb': FakeParam('attr', dict())},
+ })
+
+ range_node = Node(graph, 'range')
+
+ tf_range_infer(range_node)
+ out_value = graph.node['output']['value']
+ self.assertIsNone(out_value)
+
+ def test_non_constant_delta_range_infer(self):
+ graph = build_graph(nodes_attributes, edges,
+ {'start': {'value': np.array([1])},
+ 'limit': {'value': np.array([10])},
+ 'delta': {},
+ 'range': {'pb': FakeParam('attr', dict())},
+ })
+
+ range_node = Node(graph, 'range')
+
+ tf_range_infer(range_node)
+ out_value = graph.node['output']['value']
+ self.assertIsNone(out_value)
diff --git a/model-optimizer/mo/front/common/partial_infer/roipooling_test.py b/model-optimizer/mo/front/common/partial_infer/roipooling_test.py
new file mode 100644
index 000000000..f6b9ebadc
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/roipooling_test.py
@@ -0,0 +1,92 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.roipooling import roipooling_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'kind': 'data'},
+ 'node_2': {'kind': 'data'},
+ 'node_3': {'kind': 'data'},
+ 'node_4': {'kind': 'data'},
+ 'roipool': {'type': 'ROIPooling', 'kind': 'op', 'pooled_h': None, 'pooled_w': None},
+ 'output': {'value': None, 'kind': 'data'}
+ }
+
+
+class TestRoipoolingInfer(unittest.TestCase):
+ def test_roipooling_infer_ideal(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'roipool'),
+ ('node_2', 'roipool'),
+ ('roipool', 'output')],
+ {'output': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 256, 20, 20])},
+ 'node_2': {'shape': np.array([150, 5])},
+ 'roipool': {'pooled_h': 6, 'pooled_w': 6}
+ })
+ graph.graph['layout'] = 'NCHW'
+ roipooling_node = Node(graph, 'roipool')
+
+ roipooling_infer(roipooling_node)
+ exp_shape = np.array([150, 256, 6, 6])
+ res_shape = graph.node['output']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_roipooling_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'roipool'),
+ ('node_2', 'roipool'),
+ ('roipool', 'output')],
+ {'output': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': None},
+ 'node_2': {'shape': np.array([1, 256])},
+ 'roipool': {'pooled_h': 6, 'pooled_w': 6}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ roipooling_node = Node(graph, 'roipool')
+
+ roipooling_infer(roipooling_node)
+ self.assertIsNone(graph.node['output']['shape'])
+
+ def test_roipooling_infer_tf(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'roipool'),
+ ('node_2', 'roipool'),
+ ('node_3', 'roipool'),
+ ('node_4', 'roipool'),
+ ('roipool', 'output')],
+ {'output': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 20, 20, 256])},
+ 'node_2': {'shape': np.array([150, 5])},
+ 'node_3': {'shape': np.array([150])},
+ 'node_4': {'shape': np.array([2], dtype=np.int64), 'value': np.array([7, 6],
+ dtype=np.int64)},
+ })
+ graph.graph['layout'] = 'NHWC'
+ roipooling_node = Node(graph, 'roipool')
+
+ roipooling_infer(roipooling_node)
+ exp_shape = np.array([150, 7, 6, 256])
+ res_shape = graph.node['output']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
diff --git a/model-optimizer/mo/front/common/partial_infer/slice_test.py b/model-optimizer/mo/front/common/partial_infer/slice_test.py
new file mode 100644
index 000000000..cdd674d0e
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/slice_test.py
@@ -0,0 +1,381 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.slice import caffe_slice_infer, tf_strided_slice_infer, \
+ convert_negative_indices, mxnet_slice_axis_infer
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'value': None, 'kind': 'data'},
+ 'Slice_node': {'type': 'Slice', 'kind': 'op'},
+ 'node_2': {'value': None, 'kind': 'data'},
+ 'node_3': {'value': None, 'kind': 'data'},
+ 'node_4': {'value': None, 'kind': 'data'},
+ # StridedSlice node with attrs
+ 'sslice_input': {'value': None, 'shape': None, 'kind': 'data'},
+ 'sslice_1': {'type': 'StridedSlice', 'value': None, 'kind': 'op', 'op': 'StridedSlice'},
+ 'sslice_begin_1': {'value': None, 'shape': None, 'kind': 'data'},
+ 'sslice_end_1': {'value': None, 'shape': None, 'kind': 'data'},
+ 'sslice_stride_1': {'value': None, 'shape': None, 'kind': 'data'},
+ 'sslice_data_1': {'value': None, 'shape': None, 'kind': 'data'},
+ # TF slice
+ 'tf_slice_input': {'value': None, 'shape': None, 'kind': 'data'},
+ 'tf_slice_begin': {'value': None, 'shape': None, 'kind': 'data'},
+ 'tf_slice_size': {'value': None, 'shape': None, 'kind': 'data'},
+ 'tf_slice': {'kind': 'op'},
+ 'tf_slice_output': {'value': None, 'shape': None, 'kind': 'data'},
+ }
+
+tf_slice_edges = [('tf_slice_input', 'tf_slice'), ('tf_slice_begin', 'tf_slice'), ('tf_slice_size', 'tf_slice'),
+ ('tf_slice', 'tf_slice_output')]
+
+
+class TestSSliceInfer(unittest.TestCase):
+ def test_slice_infer_ideal(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'Slice_node'),
+ ('Slice_node', 'node_2'),
+ ('Slice_node', 'node_3')],
+ {'node_1': {'shape': np.array([1, 288, 56, 56])},
+ 'node_2': {'is_output': True, 'shape': None},
+ 'node_3': {'is_output': True, 'shape': None},
+ 'Slice_node': {'axis': 1, 'slice_point': np.array([256])}
+ })
+
+ slice_node = Node(graph, 'Slice_node')
+
+ caffe_slice_infer(slice_node)
+ exp_shape1 = np.array([1, 256, 56, 56])
+ exp_shape2 = np.array([1, 32, 56, 56])
+ res_shape1 = graph.node['node_2']['shape']
+ res_shape2 = graph.node['node_3']['shape']
+
+ for i in range(0, len(exp_shape1)):
+ self.assertEqual(exp_shape1[i], res_shape1[i])
+
+ for i in range(0, len(exp_shape2)):
+ self.assertEqual(exp_shape2[i], res_shape2[i])
+
+ def test_slice_infer_no_slice_point(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'Slice_node'),
+ ('Slice_node', 'node_2'),
+ ('Slice_node', 'node_3')],
+ {'node_1': {'shape': np.array([1, 288, 56, 56])},
+ 'node_2': {'is_output': True, 'shape': None},
+ 'node_3': {'is_output': True, 'shape': None},
+ 'Slice_node': {'axis': 1, 'slice_point': []}
+ })
+
+ slice_node = Node(graph, 'Slice_node')
+
+ caffe_slice_infer(slice_node)
+ exp_shape = np.array([1, 144, 56, 56])
+ res_shape1 = graph.node['node_2']['shape']
+ res_shape2 = graph.node['node_3']['shape']
+
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape1[i])
+
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape2[i])
+
+ def test_slice_infer_3_outs_no_slice_point(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'Slice_node'),
+ ('Slice_node', 'node_2'),
+ ('Slice_node', 'node_3'),
+ ('Slice_node', 'node_4')],
+ {'node_1': {'shape': np.array([1, 288, 56, 56])},
+ 'node_2': {'is_output': True, 'shape': None},
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_4': {'is_output': True, 'shape': None},
+ 'Slice_node': {'axis': 1, 'slice_point': []}
+ })
+
+ slice_node = Node(graph, 'Slice_node')
+
+ caffe_slice_infer(slice_node)
+ exp_shape = np.array([1, 96, 56, 56])
+ res_shape1 = graph.node['node_2']['shape']
+ res_shape2 = graph.node['node_3']['shape']
+ res_shape3 = graph.node['node_4']['shape']
+
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape1[i])
+
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape2[i])
+
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape3[i])
+
+ def test_slice_infer_3_outs(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'Slice_node'),
+ ('Slice_node', 'node_2'),
+ ('Slice_node', 'node_3'),
+ ('Slice_node', 'node_4')],
+ {'node_1': {'shape': np.array([1, 288, 56, 56])},
+ 'node_2': {'is_output': True, 'shape': None},
+ 'node_3': {'is_output': True, 'shape': None},
+ 'node_4': {'is_output': True, 'shape': None},
+ 'Slice_node': {'axis': 1, 'slice_point': [100, 150]}
+ })
+
+ slice_node = Node(graph, 'Slice_node')
+
+ caffe_slice_infer(slice_node)
+ exp_shape1 = np.array([1, 100, 56, 56])
+ exp_shape2 = np.array([1, 50, 56, 56])
+ exp_shape3 = np.array([1, 138, 56, 56])
+ res_shape1 = graph.node['node_2']['shape']
+ res_shape2 = graph.node['node_3']['shape']
+ res_shape3 = graph.node['node_4']['shape']
+
+ for i in range(0, len(exp_shape1)):
+ self.assertEqual(exp_shape1[i], res_shape1[i])
+
+ for i in range(0, len(exp_shape2)):
+ self.assertEqual(exp_shape2[i], res_shape2[i])
+
+ for i in range(0, len(exp_shape3)):
+ self.assertEqual(exp_shape3[i], res_shape3[i])
+
+
+class TestTFStridedSliceInfer(unittest.TestCase):
+ def build_test_graph2(self):
+ return build_graph(nodes_attributes,
+ [('sslice_input', 'sslice_1'),
+ ('sslice_begin_1', 'sslice_1'),
+ ('sslice_end_1', 'sslice_1'),
+ ('sslice_stride_1', 'sslice_1'),
+ ('sslice_1', 'sslice_data_1'),
+ ],
+ {'sslice_data_1': {'is_output': True},
+ 'sslice_input': {'value': np.array([1, 34, 34, 62]),
+ 'shape': np.array([3])},
+ 'sslice_begin_1': {'value': np.array([0]), 'shape': np.array([1])},
+ 'sslice_end_1': {'value': np.array([4]), 'shape': np.array([1])},
+ 'sslice_stride_1': {'value': np.array([1]), 'shape': np.array([1])},
+ 'sslice_1': {'shrink_axis_mask': 0, 'ellipsis_mask': 0, 'new_axis_mask': 0,
+ 'begin_mask': 0, 'end_mask': 0},
+ })
+
+ def build_test_graph(self):
+ return build_graph(nodes_attributes,
+ [('sslice_input', 'sslice_1'),
+ ('sslice_begin_1', 'sslice_1'),
+ ('sslice_end_1', 'sslice_1'),
+ ('sslice_stride_1', 'sslice_1'),
+ ('sslice_1', 'sslice_data_1'),
+ ],
+ {'sslice_data_1': {'is_output': True},
+ 'sslice_input': {'value': None, 'shape': np.array([1, 35, 35, 3])},
+ 'sslice_begin_1': {'value': np.array([0, 0, 0, 0]), 'shape': np.array([4])},
+ 'sslice_end_1': {'value': np.array([1, 34, 30, 2]), 'shape': np.array([4])},
+ 'sslice_stride_1': {'value': np.array([1, 1, 1, 1]),
+ 'shape': np.array([4])},
+ 'sslice_1': {'shrink_axis_mask': 0, 'ellipsis_mask': 0, 'new_axis_mask': 0,
+ 'begin_mask': 0, 'end_mask': 0},
+ })
+
+ def build_test_graph_dim_beg(self):
+ return build_graph(nodes_attributes,
+ [('sslice_input', 'sslice_1'),
+ ('sslice_begin_1', 'sslice_1'),
+ ('sslice_end_1', 'sslice_1'),
+ ('sslice_stride_1', 'sslice_1'),
+ ('sslice_1', 'sslice_data_1'),
+ ],
+ {'sslice_data_1': {'is_output': True},
+ 'sslice_input': {'value': np.array([[1, 34, 34, 62]]),
+ 'shape': np.array([1, 4])},
+ 'sslice_begin_1': {'value': np.array([0]), 'shape': np.array([1])},
+ 'sslice_end_1': {'value': np.array([4]), 'shape': np.array([1])},
+ 'sslice_stride_1': {'value': np.array([1]), 'shape': np.array([1])},
+ 'sslice_1': {'shrink_axis_mask': 0, 'ellipsis_mask': 0, 'new_axis_mask': 0,
+ 'begin_mask': 0, 'end_mask': 0},
+ })
+
+
+ def test_slice_infer_1(self):
+ graph = self.build_test_graph()
+ node = Node(graph, 'sslice_1')
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([1, 34, 30, 2])), 'Wrong output shape detected')
+
+ def test_slice_infer_2(self):
+ graph = self.build_test_graph()
+ node = Node(graph, 'sslice_1')
+ node.end_mask = 6 # 0110
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([1, 35, 35, 2])), 'Wrong output shape detected')
+
+ def test_slice_infer_3(self):
+ graph = self.build_test_graph()
+ node = Node(graph, 'sslice_1')
+ node.in_node(1).value = np.array([0, 10, 10, 0])
+ node.end_mask = 6 # 0110
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([1, 25, 25, 2])), 'Wrong output shape detected')
+
+ def test_slice_infer_4(self):
+ graph = self.build_test_graph()
+ node = Node(graph, 'sslice_1')
+ node.in_node(1).value = np.array([0, 10, 10, 0])
+ node.begin_mask = 6 # 0110
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([1, 34, 30, 2])), 'Wrong output shape detected')
+
+ def test_slice_infer_5(self):
+ graph = self.build_test_graph()
+ node = Node(graph, 'sslice_1')
+ node.in_node(1).value = np.array([0, 10, 10, 0])
+ node.begin_mask = 15 # 1111
+ node.end_mask = 15 # 1111
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([1, 35, 35, 3])), 'Wrong output shape detected')
+
+ def test_slice_infer_6(self):
+ graph = self.build_test_graph2()
+ node = Node(graph, 'sslice_1')
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([4])), 'Wrong output shape detected')
+ self.assertTrue(np.array_equal(node.out_node().value, np.array([1, 34, 34, 62])), 'Wrong output value detected')
+
+ def test_slice_infer_7(self):
+ graph = self.build_test_graph2()
+ node = Node(graph, 'sslice_1')
+ node.in_node(1).value = np.array([1])
+ node.in_node(2).value = np.array([3])
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([2])), 'Wrong output shape detected')
+ self.assertTrue(np.array_equal(node.out_node().value, np.array([34, 34])), 'Wrong output value detected')
+
+ def test_slice_infer_8(self):
+ graph = self.build_test_graph2()
+ node = Node(graph, 'sslice_1')
+ node.new_axis_mask = 1
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([1, 4])), 'Wrong output shape detected')
+ self.assertTrue(np.array_equal(node.out_node().value, np.array([[1, 34, 34, 62]])),
+ 'Wrong output value detected')
+
+ def test_slice_infer_9(self):
+ graph = self.build_test_graph()
+ node = Node(graph, 'sslice_1')
+ node.begin_mask = 15 # 1111
+ node.end_mask = 15 # 1111
+ node.shrink_axis_mask = 1
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([35, 35, 3])), 'Wrong output shape detected')
+
+ def test_slice_infer_10(self):
+ graph = self.build_test_graph()
+ node = Node(graph, 'sslice_1')
+ node.begin_mask = 15 # 1111
+ node.end_mask = 15 # 1111
+ node.shrink_axis_mask = 1
+ node.new_axis_mask = 8
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([35, 35, 1, 3])), 'Wrong output shape detected')
+
+ def test_slice_infer_11(self):
+ graph = self.build_test_graph()
+ node = Node(graph, 'sslice_1')
+ node.begin_mask = 15 # 1111
+ node.end_mask = 15 # 1111
+ node.shrink_axis_mask = 5 # 0101
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([35, 3])), 'Wrong output shape detected')
+
+ def test_slice_infer_12(self):
+ graph = self.build_test_graph()
+ node = Node(graph, 'sslice_1')
+ node.begin_mask = 15 # 1111
+ node.end_mask = 15 # 1111
+ node.shrink_axis_mask = 7 # 0111
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([3])), 'Wrong output shape detected')
+
+ def test_slice_infer_13(self):
+ graph = self.build_test_graph2()
+ node = Node(graph, 'sslice_1')
+ # node.in_node(0).value = np.array([1])
+ node.in_node(1).value = np.array([1])
+ node.shrink_axis_mask = 1
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([])), 'Wrong output shape detected')
+ self.assertTrue(np.array_equal(node.out_node().value, np.array(34)), 'Wrong output shape detected')
+
+ def test_slice_infer_14(self):
+ graph = self.build_test_graph2()
+ node = Node(graph, 'sslice_1')
+ # node.in_node(0).value = np.array([1])
+ node.in_node(3).value = np.array([-1])
+ node.end_mask=1
+ node.begin_mask=1
+ node.in_node(0).shape=[4]
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([4])), 'Wrong output shape detected')
+ print(node.out_node().value)
+ self.assertTrue(np.array_equal(node.out_node().value, np.array([62, 34, 34, 1])), 'Wrong output shape detected')
+
+ def test_slice_infer_dim_beg(self):
+ graph = self.build_test_graph_dim_beg()
+ node = Node(graph, 'sslice_1')
+ # node.in_node(0).value = np.array([1])
+ node.shrink_axis_mask = 1
+ tf_strided_slice_infer(node)
+ self.assertTrue(np.array_equal(node.out_node().shape, np.array([4])), 'Wrong output shape detected')
+ self.assertTrue(np.array_equal(node.out_node().value, np.array([1, 34, 34, 62])), 'Wrong output shape detected')
+
+
+class TestConvertNegativeIndices(unittest.TestCase):
+ def test_convert_negative_indices(self):
+ dimensions = np.array([3, 4, 8, 10])
+ indices = np.array([2, 0, -3, -4])
+ convert_negative_indices(indices, dimensions)
+ self.assertTrue(np.array_equal(indices, np.array([2, 0, 5, 6])), 'Wrong dimension indices')
+
+
+class TestMXNetSliceAxisInfer(unittest.TestCase):
+ def test_slice_axis_infer_layer(self):
+ graph = build_graph(
+ {'node_1': {'name': 'data', 'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'slice_axis_node': {'name': 'slice_axis_node', 'type': 'sigmoid', 'value': None,
+ 'kind': 'op', 'op': 'slice_axis', },
+ 'node_3': {'name': 'node_3', 'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [
+ ('node_1', 'slice_axis_node'),
+ ('slice_axis_node', 'node_3'),
+ ],
+ {
+ 'node_1': {'shape': np.array([1, 1024, 19, 19])},
+ 'slice_axis_node': {'axis': 1, 'offset': 10, 'dim': 25},
+ })
+
+ slice_axis_node = Node(graph, 'slice_axis_node')
+ mxnet_slice_axis_infer(slice_axis_node)
+ res_shape = [1, 15, 19, 19]
+ for i in range(0, len(graph.node['node_3']['shape'])):
+ self.assertEqual(graph.node['node_3']['shape'][i], res_shape[i])
diff --git a/model-optimizer/mo/front/common/partial_infer/split_test.py b/model-optimizer/mo/front/common/partial_infer/split_test.py
new file mode 100644
index 000000000..a81b57ae5
--- /dev/null
+++ b/model-optimizer/mo/front/common/partial_infer/split_test.py
@@ -0,0 +1,207 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.split import tf_split_infer, tf_unpack_infer, tf_split_v_infer, split
+from mo.front.common.partial_infer.utils import int64_array
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph, build_graph_with_edge_attrs
+
+
+class TestTFSplitInfer(unittest.TestCase):
+ graph = None
+
+ def setUp(self):
+ self.graph = build_graph({'split_dim': {'value': None, 'kind': 'data'},
+ 'data_to_split': {'value': None, 'shape': None, 'kind': 'data'},
+ 'split_node': {'kind': 'op', 'op': 'Split', 'num_split': 3, 'axis': None},
+ 'out_data_1': {'value': None, 'shape': None, 'kind': 'data'},
+ 'out_data_2': {'value': None, 'shape': None, 'kind': 'data'},
+ 'out_data_3': {'value': None, 'shape': None, 'kind': 'data'},
+ },
+ [('split_dim', 'split_node'),
+ ('data_to_split', 'split_node'),
+ ('split_node', 'out_data_1'),
+ ('split_node', 'out_data_2'),
+ ('split_node', 'out_data_3'),
+ ])
+
+ def test_tf_split_infer(self):
+ split_node = Node(self.graph, 'split_node')
+ self.graph.node['split_dim']['value'] = np.array(1)
+ self.graph.node['data_to_split']['shape'] = int64_array([2, 12, 25, 30])
+
+ tf_split_infer(split_node)
+ exp_shape = int64_array([2, 4, 25, 30])
+ for out_node in split_node.out_nodes().values():
+ self.assertTrue(np.all(exp_shape == out_node.shape))
+ self.assertEqual(1, split_node.input_port)
+
+ def test_tf_split_infer_negative_index(self):
+ split_node = Node(self.graph, 'split_node')
+ self.graph.node['split_dim']['value'] = np.array(-3)
+ self.graph.node['data_to_split']['shape'] = int64_array([2, 12, 25, 30])
+
+ tf_split_infer(split_node)
+ exp_shape = int64_array([2, 4, 25, 30])
+ for out_node in split_node.out_nodes().values():
+ self.assertTrue(np.all(exp_shape == out_node.shape))
+ self.assertEqual(1, split_node.input_port)
+
+ def test_tf_split_infer_unknown_index(self):
+ split_node = Node(self.graph, 'split_node')
+ self.graph.node['data_to_split']['shape'] = int64_array([2, 12, 25, 30])
+
+ tf_split_infer(split_node)
+ for out_node in split_node.out_nodes().values():
+ self.assertIsNone(out_node.shape)
+
+ def test_tf_split_infer_input_shape_is_None(self):
+ split_node = Node(self.graph, 'split_node')
+ self.graph.node['split_dim']['value'] = np.array(1)
+
+ tf_split_infer(split_node)
+ for out_node in split_node.out_nodes().values():
+ self.assertIsNone(out_node.shape)
+
+ def test_tf_split_infer_wrong_num_split(self):
+ split_node = Node(self.graph, 'split_node')
+ self.graph.node['split_dim']['value'] = np.array(0)
+ self.graph.node['data_to_split']['shape'] = int64_array([2, 12, 25, 30])
+
+ tf_split_infer(split_node)
+ for out_node in split_node.out_nodes().values():
+ self.assertIsNone(out_node.shape)
+
+
+class TestTFSplitVInfer(unittest.TestCase):
+ graph = None
+
+ def setUp(self):
+ self.graph = build_graph({'data_to_split': {'value': None, 'shape': None, 'kind': 'data'},
+ 'size_splits': {'value': [3, 5, 4], 'kind': 'data'},
+ 'split_dim': {'value': None, 'kind': 'data'},
+ 'split_node': {'kind': 'op', 'op': 'Split', 'axis': None},
+ 'out_data_1': {'value': None, 'shape': None, 'kind': 'data'},
+ 'out_data_2': {'value': None, 'shape': None, 'kind': 'data'},
+ 'out_data_3': {'value': None, 'shape': None, 'kind': 'data'},
+ },
+ [('data_to_split', 'split_node'),
+ ('size_splits', 'split_node'),
+ ('split_dim', 'split_node'),
+ ('split_node', 'out_data_1'),
+ ('split_node', 'out_data_2'),
+ ('split_node', 'out_data_3'),
+ ])
+
+ def test_tf_split_infer_three_inputs(self):
+ split_node = Node(self.graph, 'split_node')
+ self.graph.node['split_dim']['value'] = np.array(1)
+ self.graph.node['data_to_split']['shape'] = int64_array([2, 12, 25, 30])
+
+ tf_split_v_infer(split_node)
+ exp_shape = [int64_array([2, 3, 25, 30]), int64_array([2, 5, 25, 30]), int64_array([2, 4, 25, 30])]
+ for ind, out_node in split_node.out_nodes().items():
+ self.assertTrue(np.all(exp_shape[ind] == out_node.shape))
+
+
+class TestTFUnpack(unittest.TestCase):
+ graph = None
+
+ def setUp(self):
+ self.graph = build_graph({'data_to_split': {'value': None, 'shape': None, 'kind': 'data'},
+ 'unpack': {'kind': 'op', 'op': 'Split', 'num_split': 3, 'axis': None},
+ 'out_data_1': {'value': None, 'shape': None, 'kind': 'data'},
+ 'out_data_2': {'value': None, 'shape': None, 'kind': 'data'},
+ 'out_data_3': {'value': None, 'shape': None, 'kind': 'data'},
+ 'out_data_4': {'value': None, 'shape': None, 'kind': 'data'},
+ },
+ [('data_to_split', 'unpack'),
+ ('unpack', 'out_data_1'),
+ ('unpack', 'out_data_2'),
+ ('unpack', 'out_data_3'),
+ ])
+
+ def test_tf_unpack_infer(self):
+ unpack_node = Node(self.graph, 'unpack')
+ self.graph.node['unpack']['axis'] = np.array(1)
+ self.graph.node['data_to_split']['shape'] = int64_array([2, 3, 25, 30])
+
+ tf_unpack_infer(unpack_node)
+ exp_shape = int64_array([2, 1, 25, 30])
+ for out_node in unpack_node.out_nodes().values():
+ self.assertTrue(np.all(exp_shape == out_node.shape))
+
+ def test_tf_unpack_infer_default_number_of_pieces(self):
+ unpack_node = Node(self.graph, 'unpack')
+ self.graph.node['unpack']['axis'] = np.array(1)
+ self.graph.node['unpack']['num_split'] = None
+ self.graph.node['data_to_split']['shape'] = int64_array([2, 3, 25, 30])
+
+ tf_unpack_infer(unpack_node)
+ exp_shape = int64_array([2, 1, 25, 30])
+ for out_node in unpack_node.out_nodes().values():
+ self.assertTrue(np.all(exp_shape == out_node.shape))
+
+ def test_tf_unpack_infer_not_supported(self):
+ # the case when the size of the dimension being unpacked is not equal to number of pieces is not supported
+ unpack_node = Node(self.graph, 'unpack')
+ self.graph.node['unpack']['axis'] = np.array(1)
+ self.graph.node['data_to_split']['shape'] = int64_array([2, 6, 25, 30])
+
+ tf_unpack_infer(unpack_node)
+ for out_node in unpack_node.out_nodes().values():
+ self.assertIsNone(out_node.shape)
+
+
+class TestSplitFunc(unittest.TestCase):
+ graph = None
+
+ def setUp(self):
+ self.graph = build_graph_with_edge_attrs(
+ {'data_to_split': {'value': None, 'shape': int64_array([2, 12, 25, 44]), 'kind': 'data'},
+ 'split_node': {'kind': 'op', 'op': 'Split', 'axis': None},
+ 'out_data_2': {'value': None, 'shape': None, 'kind': 'data'},
+ 'out_data_5': {'value': None, 'shape': None, 'kind': 'data'},
+ 'out_data_7': {'value': None, 'shape': None, 'kind': 'data'},
+ },
+ [('data_to_split', 'split_node', {'in': 0}),
+ ('split_node', 'out_data_2', {'out': 2}),
+ ('split_node', 'out_data_5', {'out': 5}),
+ ('split_node', 'out_data_7', {'out': 7}),
+ ])
+
+ def test_split_non_sequential_output_port(self):
+ split(Node(self.graph, 'data_to_split'), Node(self.graph, 'split_node'), -1, [3, 2, 7, 5, 6, 4, 9, 8])
+ self.assertTrue(np.all(Node(self.graph, 'out_data_2').shape == [2, 12, 25, 7]))
+ self.assertTrue(np.all(Node(self.graph, 'out_data_5').shape == [2, 12, 25, 4]))
+ self.assertTrue(np.all(Node(self.graph, 'out_data_7').shape == [2, 12, 25, 8]))
+
+ def test_split_value_infer_non_sequential_output_port(self):
+ data_node = Node(self.graph, 'data_to_split')
+ value = np.array(range(2 * 12 * 25 * 44)).reshape(data_node.shape)
+ data_node.value = value.copy()
+ split(data_node, Node(self.graph, 'split_node'), -1, [3, 2, 7, 5, 6, 4, 9, 8])
+ self.assertTrue(np.all(Node(self.graph, 'out_data_2').shape == [2, 12, 25, 7]))
+ self.assertTrue(np.all(Node(self.graph, 'out_data_5').shape == [2, 12, 25, 4]))
+ self.assertTrue(np.all(Node(self.graph, 'out_data_7').shape == [2, 12, 25, 8]))
+
+ self.assertTrue(np.all(Node(self.graph, 'out_data_2').value == value[:, :, :, 5:12]))
+ self.assertTrue(np.all(Node(self.graph, 'out_data_5').value == value[:, :, :, 23:27]))
+ self.assertTrue(np.all(Node(self.graph, 'out_data_7').value == value[:, :, :, 36:]))
diff --git a/model-optimizer/mo/front/extractor_test.py b/model-optimizer/mo/front/extractor_test.py
new file mode 100644
index 000000000..5fcb5eb70
--- /dev/null
+++ b/model-optimizer/mo/front/extractor_test.py
@@ -0,0 +1,602 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+from generator import generator, generate
+
+from mo.front.extractor import input_user_data_repack, output_user_data_repack, extract_port_from_string, \
+ update_ie_fields, add_input_op
+from mo.front.extractor import spatial_attr_getter, add_input_ops, attr_getter, CaffePythonFrontExtractorOp, \
+ add_output_ops
+from mo.graph.graph import Node
+from mo.middle.passes import eliminate
+from mo.utils.error import Error
+from mo.utils.unittest.extractors import FakeMultiParam
+from mo.utils.unittest.graph import build_graph, build_graph_with_edge_attrs, build_graph_with_attrs, compare_graphs
+
+
+class FakePythonParam:
+ def __init__(self, param: FakeMultiParam):
+ self.__setattr__('python_param', param)
+
+
+nodes_attributes = {'input': {'kind': 'data'},
+ 'pool_1': {'type': 'Pooling', 'kind': 'op'},
+ 'output': {'kind': 'data'}
+ }
+
+
+class UpdateIEFieldsTest(unittest.TestCase):
+ def test_default_update_ie_fields(self):
+ update_ie_fields({}, ir_version=None)
+
+ def test_not_set_update_ie_fields(self):
+ with self.assertRaisesRegex(Error, 'Unrecognized IR version.*'):
+ update_ie_fields({}, ir_version='abracadabra')
+
+
+class TestExtractor(unittest.TestCase):
+ def test_spatial_attr_getter(self):
+ input_shape = np.array([1, 125, 13, 13])
+ params = {
+ 'kernel': np.array([1, 1, 1, 2]),
+ 'pad': np.array([1, 1, 3, 4]),
+ 'stride': np.array([1, 1, 2, 3]),
+ }
+ graph = build_graph(nodes_attributes,
+ [('input', 'pool_1'),
+ ('pool_1', 'output')],
+ {'input': {'shape': input_shape},
+ 'pool_1': {**params, 'spatial_dims': [2, 3]},
+ 'output': {'is_output': True, 'shape': None}})
+ pool_1_node = Node(graph, 'pool_1')
+ for param in params.keys():
+ if type(params[param]) is np.ndarray:
+ port_lambda = lambda x: x
+ self.assertEqual(params[param][2],
+ spatial_attr_getter(pool_1_node, field=param, dim=0, post=port_lambda))
+ self.assertEqual(params[param][3],
+ spatial_attr_getter(pool_1_node, field=param, dim=1, post=port_lambda))
+
+ def test_attr_getter(self):
+ nodes = {'input': {'kind': 'data'},
+ 'reshape': {'type': 'Reshape', 'kind': 'op'},
+ 'output': {'kind': 'data'}
+ }
+ input_shape = np.array([1, 125, 13, 13])
+ params = {
+ 'dim': [1, 1, 2, 3],
+ 'max_size': np.array([3, 2, 1, 0])
+ }
+ expect_params = {
+ 'dim': "1,1,2,3",
+ 'max_size': "3,2,1,0",
+ }
+ graph = build_graph(nodes,
+ [('input', 'reshape'),
+ ('reshape', 'output')],
+ {'input': {'shape': input_shape},
+ 'reshape': {**params, 'spatial_dims': [2, 3]},
+ 'output': {'is_output': True, 'shape': None}})
+ pool_1_node = Node(graph, 'reshape')
+ for param in params.keys():
+ if type(params[param]) is list:
+ self.assertEqual(expect_params[param],
+ attr_getter(pool_1_node, param))
+
+
+class TestAddInputOp(unittest.TestCase):
+ nodes = [
+ ('op_node', {'kind': 'op'}),
+ ('future_input', {'kind': 'op'}),
+ ('another_node', {'kind': 'op'}),
+ ]
+ edges = [('future_input', 'op_node', {'in': 1, 'out': 0}),
+ ('another_node', 'op_node', {'in': 0, 'out': 0})]
+
+ def test_in_port_no_data(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges)
+ new_input_shape = np.array([1, 2, 3, 4])
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges[1:],
+ new_nodes_with_attrs=[('input_node', {'kind': 'op', 'op': 'Placeholder',
+ 'shape': new_input_shape})],
+ new_edges_with_attrs=[('input_node', 'op_node', {'in': 1, 'out': 0})])
+ add_input_op(graph, 'op_node', 1, data=False, shape=new_input_shape)
+ graph.remove_edge('future_input', 'op_node')
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='op_node')
+ self.assertTrue(flag, resp)
+
+ def test_in_port_with_data(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges)
+ new_input_shape = np.array([1, 2, 3, 4])
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes, edges_with_attrs=self.edges[1:],
+ new_nodes_with_attrs=[('input_node', {'kind': 'op', 'op': 'Placeholder',
+ 'shape': new_input_shape}),
+ ('input_data', {'kind': 'data'})],
+ new_edges_with_attrs=[('input_node', 'input_data', {'in': 0, 'out': 0}),
+ ('input_data', 'op_node', {'in': 1, 'out': 0})])
+ add_input_op(graph, 'op_node', 1, data=True, shape=new_input_shape)
+ graph.remove_edge('future_input', 'op_node')
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='op_node')
+ self.assertTrue(flag, resp)
+
+ nodes_out = [
+ ('op_node', {'kind': 'op'}),
+ ('future_input', {'kind': 'op'}),
+ ('another_node', {'kind': 'op'}),
+ ]
+ edges_out = [('op_node', 'future_input', {'in': 0, 'out': 1}),
+ ('op_node', 'another_node', {'in': 0, 'out': 0})]
+
+ def test_out_port_no_data(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes_out, edges_with_attrs=self.edges_out)
+ new_input_shape = np.array([1, 2, 3, 4])
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes_out, edges_with_attrs=self.edges_out[1:],
+ new_nodes_with_attrs=[('input_node', {'kind': 'op', 'op': 'Placeholder',
+ 'shape': new_input_shape})],
+ new_edges_with_attrs=[('input_node', 'future_input', {'in': 0, 'out': 0})])
+ add_input_op(graph, 'op_node', 1, data=False, shape=new_input_shape, is_out_port=True)
+ graph.remove_edge('op_node', 'future_input')
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='another_node')
+ self.assertTrue(flag, resp)
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='future_input')
+ self.assertTrue(flag, resp)
+
+ def test_out_port_with_data(self):
+ graph = build_graph_with_attrs(nodes_with_attrs=self.nodes_out, edges_with_attrs=self.edges_out[1:],
+ new_nodes_with_attrs=[('input_data', {'kind': 'data', 'shape': None})],
+ new_edges_with_attrs=[('op_node', 'input_data', {'out': 1, 'in': 0}),
+ ('input_data', 'future_input', {'in': 0, 'out': 0})])
+ new_input_shape = np.array([1, 2, 3, 4])
+ graph_ref = build_graph_with_attrs(nodes_with_attrs=self.nodes_out, edges_with_attrs=self.edges_out[1:],
+ new_nodes_with_attrs=[('input_node', {'kind': 'op', 'op': 'Placeholder',
+ 'shape': new_input_shape}),
+ ('input_data', {'kind': 'data', 'shape': None})],
+ new_edges_with_attrs=[('input_node', 'input_data', {'in': 0, 'out': 0}),
+ ('input_data', 'future_input', {'in': 0, 'out': 0})])
+ add_input_op(graph, 'op_node', 1, data=True, shape=new_input_shape, is_out_port=True)
+ graph.remove_edge('op_node', 'input_data')
+
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='another_node')
+ self.assertTrue(flag, resp)
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='future_input')
+ self.assertTrue(flag, resp)
+
+
+class TestInputAddition(unittest.TestCase):
+ # Tests for input
+ nodes = {'node_1': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'relu_1': {'type': 'ReLU', 'kind': 'op', 'op': 'NotPlaceholder'},
+ }
+ edges = [
+ ('node_1', 'conv_1'),
+ ('conv_1', 'relu_1'),
+ ]
+
+ def test_none_out_port_raise(self):
+ graph = build_graph(self.nodes, self.edges)
+ shape = np.array([1, 2, 3, 4])
+ inputs = {'conv_1': [{'shape': shape, 'out': None}]}
+ with self.assertRaisesRegex(Error, 'Output port for input node conv_1 should be specified, it cannot be None!'):
+ add_input_ops(graph=graph, user_defined_inputs=inputs, before_infer=True)
+
+ def test_wrong_output_port_raise(self):
+ graph = build_graph(self.nodes, self.edges)
+ shape = np.array([1, 2, 3, 4])
+ inputs = {'conv_1': [{'shape': shape, 'out': 5}]}
+ with self.assertRaisesRegex(Error, 'Output port index 5 is out of number of available output ports for node'):
+ add_input_ops(graph=graph, user_defined_inputs=inputs, before_infer=True)
+
+ def test_wrong_input_port_raise(self):
+ graph = build_graph(self.nodes, self.edges)
+ shape = np.array([1, 2, 3, 4])
+ inputs = {'conv_1': [{'shape': shape, 'in': 5}]}
+ with self.assertRaisesRegex(Error, 'Input port index 5 is out of number of available input ports for node'):
+ add_input_ops(graph=graph, user_defined_inputs=inputs, before_infer=True)
+
+ def test_one_input_one_shape(self):
+ shape = np.array([1, 2, 3, 4])
+ inputs = {'conv_1': [{'shape': shape}]}
+ nodes = {
+ 'old_input': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'relu_1': {'type': 'ReLU', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'output': {'type': 'SoftMax', 'kind': 'op', 'op': 'NotPlaceholder'}
+ }
+ edges = [
+ ('old_input', 'conv_1'),
+ ('conv_1', 'relu_1'),
+ ('relu_1', 'output')
+ ]
+ graph = build_graph(nodes, edges)
+ add_input_ops(graph=graph, user_defined_inputs=inputs, before_infer=True)
+ new_input = list(graph.in_edges('conv_1'))[0][0]
+ self.assertFalse(graph.node['old_input']['is_input'])
+ self.assertTrue(graph.node[new_input]['is_input'])
+ self.assertTrue((new_input, 'conv_1') in graph.edges())
+ self.assertTrue(('old_input', 'conv_1') not in graph.edges())
+ shapes_are_equal = np.array_equal(graph.node[new_input]['shape'], shape)
+ self.assertTrue(shapes_are_equal)
+
+ def test_one_input_no_shape(self):
+ shape = None
+ inputs = {'conv_1': [{'shape': shape}]}
+ nodes = {
+ 'old_input': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'old_input_data': {'kind': 'data', 'value': None, 'shape': np.array([-1, 224, 224, 3])},
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'conv_1_data': {'kind': 'data', 'value': True, 'shape': np.array([-1, 224, 224, 3])},
+ 'relu_1': {'type': 'ReLU', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'relu_1_data': {'kind': 'data', 'value': None, 'shape': np.array([-1, 112, 112, 64])},
+ 'output': {'type': 'SoftMax', 'kind': 'op', 'op': 'NotPlaceholder', 'is_output': True},
+ 'output_data': {'name': 'output_data', 'kind': 'data', 'shape': np.array([-1, 112, 112, 64])}
+ }
+ edges = [
+ ('old_input', 'old_input_data'),
+ ('old_input_data', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'relu_1'),
+ ('relu_1', 'relu_1_data'),
+ ('relu_1_data', 'output'),
+ ('output', 'output_data')
+ ]
+ graph = build_graph(nodes, edges)
+ add_input_ops(graph=graph, user_defined_inputs=inputs, before_infer=False)
+ new_input = list(graph.in_edges(list(graph.in_edges('conv_1'))[0][0]))[0][0]
+ new_input_data = list(graph.in_edges('conv_1'))[0][0]
+ self.assertFalse(graph.node['old_input']['is_input'])
+ self.assertTrue(graph.node[new_input]['is_input'])
+ self.assertTrue((new_input_data, 'conv_1') in graph.edges())
+ self.assertTrue(('old_input_data', 'conv_1') not in graph.edges())
+ self.assertIsNotNone(graph.node[new_input_data]['shape'])
+
+ def test_two_inputs_two_shapes_positive_1(self):
+ shape_1 = [1, 2, 3, 4]
+ shape_2 = [4, 3, 2, 1]
+ inputs = {'node_1': [{'shape': shape_1}], 'node_4': [{'shape': shape_2}]}
+ nodes = {
+ 'input_1': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'input_2': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'node_1': {'type': 'Identity', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'node_2': {'type': 'Identity', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'node_3': {'type': 'Identity', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'node_4': {'type': 'Identity', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'output': {'type': 'Identity', 'kind': 'op', 'op': 'OpOutput', 'is_output': True}
+ }
+ edges = [
+ ('input_1', 'node_1'),
+ ('node_1', 'node_2'),
+ ('node_3', 'output'),
+ ('input_2', 'node_4'),
+ ('node_4', 'output')
+ ]
+ graph = build_graph(nodes, edges)
+ add_input_ops(graph=graph, user_defined_inputs=inputs, before_infer=True)
+ new_input_1 = list(graph.in_edges('node_1'))[0][0]
+ new_input_2 = list(graph.in_edges('node_4'))[0][0]
+ self.assertFalse(graph.node['input_1']['is_input'])
+ self.assertTrue(graph.node[new_input_1]['is_input'])
+ self.assertTrue(graph.node[new_input_2]['is_input'])
+ self.assertTrue((new_input_1, 'node_1') in graph.edges())
+ self.assertTrue((new_input_2, 'node_4') in graph.edges())
+ self.assertListEqual(shape_1, graph.node[new_input_1]['shape'])
+ self.assertListEqual(shape_2, graph.node[new_input_2]['shape'])
+
+ def test_two_inputs_two_shapes_not_all_inputs(self):
+ shape_1 = [1, 2, 3, 4]
+ shape_2 = [4, 3, 2, 1]
+ inputs = {'node_1': [{'shape': shape_1}], 'node_4': [{'shape': shape_2}]}
+ nodes = {
+ 'input_1': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'input_2': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'node_1': {'type': 'Identity', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'node_2': {'type': 'Identity', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'node_3': {'type': 'Identity', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'node_4': {'type': 'Identity', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'output': {'type': 'Identity', 'kind': 'op', 'op': 'OpOutput', 'is_output': True},
+ 'input_3': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'}
+ }
+ edges = [
+ ('input_1', 'node_1'),
+ ('node_1', 'node_2'),
+ ('node_3', 'output'),
+ ('input_2', 'node_4'),
+ ('node_4', 'output'),
+ ('input_3', 'output')
+ ]
+ graph = build_graph(nodes, edges)
+ self.assertRaises(Error, add_input_ops, graph, inputs, True)
+
+ # Tests for cases with input/output ports cutting
+ def test_add_input_with_input_port_before_infer(self):
+ shape = np.array([1, 2, 3, 4])
+ inputs = {'conv_1': [{'shape': shape, 'in': 0}]}
+ nodes = {
+ 'old_input': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'relu_1': {'type': 'ReLU', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'output': {'type': 'SoftMax', 'kind': 'op', 'op': 'NotPlaceholder'}
+ }
+ edges = [
+ ('old_input', 'conv_1'),
+ ('conv_1', 'relu_1'),
+ ('relu_1', 'output')
+ ]
+ graph = build_graph(nodes, edges)
+ add_input_ops(graph=graph, user_defined_inputs=inputs, before_infer=True)
+
+ # Check that graph
+ graph_ref = build_graph(nodes, edges, update_attributes={'old_input': {'shape': shape}})
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='output')
+ self.assertTrue(flag, resp)
+
+ # also checks that new old_input was changed
+ new_input = list(graph.in_edges('conv_1'))[0][0]
+ self.assertFalse(graph.node['old_input']['is_input'])
+ self.assertTrue(graph.node[new_input]['is_input'])
+ self.assertTrue((new_input, 'conv_1') in graph.edges())
+ self.assertTrue(('old_input', 'conv_1') not in graph.edges())
+
+ def test_add_input_with_output_port_before_infer(self):
+ shape = np.array([1, 2, 3, 4])
+ inputs = {'conv_1': [{'shape': shape, 'out': 0}]}
+ nodes = {
+ 'old_input': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'conv_2': {'type': 'Convolution', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'relu_1': {'type': 'ReLU', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'output': {'type': 'SoftMax', 'kind': 'op', 'op': 'NotPlaceholder'}
+ }
+ edges = [
+ ('old_input', 'conv_1'),
+ ('conv_1', 'relu_1'),
+ ('conv_2', 'relu_1'),
+ ('relu_1', 'output')
+ ]
+ graph = build_graph(nodes, edges)
+ add_input_ops(graph=graph, user_defined_inputs=inputs, before_infer=True)
+
+ graph_ref = build_graph(nodes_attrs={'new_input': {'kind': 'op', 'op': 'Placeholder', 'shape': shape},
+ **nodes},
+ edges=[('new_input', 'relu_1'),
+ ('relu_1', 'output'),
+ ('conv_2', 'relu_1'),
+ ('old_input', 'conv_1'),],)
+ # Check that new input is added right (with right ports !)
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='output')
+ self.assertTrue(flag, resp)
+
+ # Check that other graph is not damaged
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='conv_1')
+ self.assertTrue(flag, resp)
+
+ # Checks for new input and edges
+ self.assertTrue('conv_1/placeholder_out_port_0' in graph.nodes())
+ new_input = 'conv_1/placeholder_out_port_0'
+ self.assertTrue(graph.node[new_input]['is_input'])
+ self.assertTrue((new_input, 'relu_1') in graph.edges())
+ self.assertTrue(('old_input', 'relu_1') not in graph.edges())
+
+ def test_add_input_with_output_port_after_infer(self):
+ shape = np.array([1, 2, 3, 4])
+ inputs = {'conv_1': [{'shape': shape, 'out': 0}]}
+ nodes = {
+ 'old_input': {'type': 'Identity', 'kind': 'op', 'op': 'Placeholder'},
+ 'inp_data' : {'kind': 'data', 'shape': shape + 1},
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'NotPlaceholder'},
+ 'conv_data': {'kind': 'data', 'shape': shape},
+ 'relu_1': {'type': 'ReLU', 'kind': 'op', 'op': 'NotPlaceholder'},
+ }
+ edges = [
+ ('old_input', 'inp_data'),
+ ('inp_data', 'conv_1'),
+ ('conv_1', 'conv_data'),
+ ('conv_data', 'relu_1'),
+ ]
+ graph = build_graph(nodes, edges)
+ add_input_ops(graph=graph, user_defined_inputs=inputs, before_infer=False)
+
+ graph_ref = build_graph(nodes_attrs={'new_input': {'kind': 'op', 'op': 'Placeholder', 'shape': shape},
+ **nodes},
+ edges=[('old_input', 'inp_data'),
+ ('inp_data', 'conv_1'),
+ ('new_input', 'conv_data'),
+ ('conv_data', 'relu_1'),
+ ],)
+ # Check that new input is added right (with right ports !)
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='relu_1')
+ self.assertTrue(flag, resp)
+
+ # Check that other graph is not damaged
+ (flag, resp) = compare_graphs(graph, graph_ref, last_node='conv_1')
+ self.assertTrue(flag, resp)
+
+ # Checks for new input and edges
+ self.assertTrue('conv_1/placeholder_out_port_0' in graph.nodes())
+ new_input = 'conv_1/placeholder_out_port_0'
+
+ self.assertTrue(graph.node[new_input]['is_input'])
+ self.assertTrue((new_input, 'conv_data') in graph.edges())
+ self.assertTrue(('conv_1', 'conv_data') not in graph.edges())
+
+@generator
+class TestOutputCut(unittest.TestCase):
+ # {'embeddings': [{'port': None}]}
+ @generate({'C': [{'port': None}]}, {'C': [{'out': 0}]}, {'C': [{'out': 1}]})
+ def test_output_port_cut(self, output):
+ nodes = {'A': {'type': 'Identity', 'kind': 'op'},
+ 'B': {'type': 'Identity', 'kind': 'op'},
+ 'C': {'type': 'Identity', 'kind': 'op'},
+ 'D': {'type': 'Identity', 'kind': 'op'},
+ 'E': {'type': 'Identity', 'kind': 'op'},
+ }
+ edges = [
+ ('A', 'C', {'in': 0, 'out': 0}),
+ ('B', 'C', {'in': 1, 'out': 0}),
+ ('C', 'D', {'in': 0, 'out': 0}),
+ ('C', 'E', {'in': 0, 'out': 1})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ sinks = add_output_ops(graph, output)
+ eliminate.graph_clean_up(graph)
+ self.assertEqual(len(Node(graph, 'C').out_nodes()), 1)
+ self.assertEqual(len(Node(graph, 'C').in_nodes()), 2)
+
+ @generate({'C': [{'in': 0}]}, {'C': [{'in': 1}]})
+ def test_output_port_cut(self, output):
+ nodes = {'A': {'op': 'Placeholder', 'kind': 'op'},
+ 'B': {'op': 'Placeholder', 'kind': 'op'},
+ 'C': {'type': 'Identity', 'kind': 'op'},
+ 'D': {'type': 'Identity', 'kind': 'op'},
+ 'E': {'type': 'Identity', 'kind': 'op'},
+ }
+ edges = [
+ ('A', 'C', {'in': 0, 'out': 0}),
+ ('B', 'C', {'in': 1, 'out': 0}),
+ ('C', 'D', {'in': 0, 'out': 0}),
+ ('C', 'E', {'in': 0, 'out': 1})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ sinks = add_output_ops(graph, output)
+ eliminate.graph_clean_up(graph)
+ self.assertEqual(len(graph.nodes()), 2)
+
+
+class TestUserDataRepack(unittest.TestCase):
+ nodes = {'A': {'name': 'Aa', 'op': 'Placeholder', 'kind': 'op'},
+ 'B': {'name': 'Bb', 'op': 'Placeholder', 'kind': 'op'},
+ 'C': {'name': 'Cc', 'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'D': {'name': 'Dd', 'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'E': {'name': 'Ee', 'type': 'Identity', 'value': None, 'kind': 'op'},
+ }
+ edges = [
+ ('A', 'C', {'in': 0, 'out': 0}),
+ ('B', 'C', {'in': 1, 'out': 0}),
+ ('C', 'D', {'in': 0, 'out': 0}),
+ ('C', 'E', {'in': 0, 'out': 1})
+ ]
+
+ def test_input_user_data_repack_none(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ input, freeze_placeholder = input_user_data_repack(graph, None, None)
+ self.assertEqual(input, None)
+ self.assertEqual(freeze_placeholder, None)
+
+ def test_input_user_data_repack_names_to_ids_list(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ input, freeze_placeholder = input_user_data_repack(graph, ['Aa', 'Bb'], None)
+ self.assertDictEqual(input, {'A': [{'shape': None, 'port': None}], 'B': [{'shape': None, 'port': None}]})
+ self.assertEqual(freeze_placeholder, None)
+
+ def test_input_user_data_repack_names_ports_in_out(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ input, freeze_placeholder = input_user_data_repack(graph, ['Aa:1', '0:Bb'], None)
+ self.assertDictEqual(input, {'A': [{'shape': None, 'out': 1}], 'B': [{'shape': None, 'in': 0}]})
+ self.assertEqual(freeze_placeholder, None)
+
+ def test_input_user_data_repack_dict_with_shapes(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ shape_1 = np.array([1, 160, 160, 3])
+ shape_2 = np.array([1, 127, 127, 3])
+ input, freeze_placeholder = input_user_data_repack(graph, {'Aa': shape_1, 'Bb': shape_2}, None)
+ self.assertDictEqual(input, {'A': [{'shape': shape_1, 'port': None}], 'B': [{'shape': shape_2, 'port': None}]})
+ self.assertEqual(freeze_placeholder, None)
+
+ def test_input_user_data_repack_dict_with_shapes_and_ports(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ shape_1 = np.array([1, 160, 160, 3])
+ shape_2 = np.array([1, 127, 127, 3])
+ input, freeze_placeholder = input_user_data_repack(graph, {'Aa:0': shape_1, 'Bb:1': shape_2}, None)
+ self.assertDictEqual(input, {'A': [{'shape': shape_1, 'out': 0}], 'B': [{'shape': shape_2, 'out': 1}]})
+ self.assertEqual(freeze_placeholder, None)
+
+ def test_freeze_placeholder_and_input(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ shape_1 = np.array([1, 160, 160, 3])
+ input, freeze_placeholder = input_user_data_repack(graph, {'Aa:0': shape_1}, {'Bb': False})
+ self.assertDictEqual(input, {'A': [{'shape': shape_1, 'out': 0}], 'B': [{'shape': None, 'port': None}]})
+ self.assertEqual(freeze_placeholder, {'B': False})
+
+ def test_error(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ self.assertRaises(Error, input_user_data_repack, graph, np.array([1, 227, 227, 3]), None)
+
+ def test_error_2(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ self.assertRaises(Error, input_user_data_repack, graph, np.array([1, 227, 227, 3]), None)
+
+ def test_error_3(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ self.assertRaises(Error, input_user_data_repack, graph, ['Bcb'], None)
+
+ def test_input_and_freeze(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ shape_1 = np.array([1, 160, 160, 3])
+ input, freeze_placeholder = input_user_data_repack(graph, shape_1, {'Bb': True})
+ self.assertDictEqual(input, {'A': [{'shape': shape_1, 'port': None}], 'B': [{'shape': None, 'port': None}]})
+ self.assertDictEqual(freeze_placeholder, {'B': True})
+
+ def test_output_user_data_repack(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ output = output_user_data_repack(graph, ['Cc'])
+ self.assertDictEqual(output, {'C': [{'port': None}]})
+
+ def test_output_user_data_repack_ports(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ output = output_user_data_repack(graph, ['Cc:1', '0:Cc'])
+ self.assertDictEqual(output, {'C': [{'out': 1}, {'in': 0}]})
+
+ def test_output_user_data_repack_none(self):
+ graph = build_graph_with_edge_attrs(self.nodes, self.edges)
+ output = output_user_data_repack(graph, None)
+ self.assertEqual(output, None)
+
+
+class TestExtractPort(unittest.TestCase):
+ def test_out_port(self):
+ name, in_port, out_port = extract_port_from_string('node_name:1')
+ self.assertEqual(name, 'node_name')
+ self.assertEqual(in_port, None)
+ self.assertEqual(out_port, 1)
+
+ def test_in_port(self):
+ name, in_port, out_port = extract_port_from_string('0:node_name')
+ self.assertEqual(name, 'node_name')
+ self.assertEqual(in_port, 0)
+ self.assertEqual(out_port, None)
+
+ def test_no_port(self):
+ name, in_port, out_port = extract_port_from_string('node_name')
+ self.assertEqual(name, 'node_name')
+ self.assertEqual(in_port, None)
+ self.assertEqual(out_port, None)
+
+ def test_non_int(self):
+ self.assertRaises(Error, extract_port_from_string, 'port:node_name')
+
+ def test_two_ports(self):
+ self.assertRaises(Error, extract_port_from_string, '1:node_name:0')
+
+
+class TestCaffePythonFrontExtractorOp(unittest.TestCase):
+ def test_get_attrs(self):
+ exp_attrs = {"test_attr_1": 12, "test_attr_2": "sdf sdf"}
+ param_str = "'test_attr_1': 12, 'test_attr_2': 'sdf sdf'"
+ attrs = CaffePythonFrontExtractorOp.get_attrs(FakePythonParam(FakeMultiParam({'param_str': param_str})))
+ self.assertEqual(exp_attrs, attrs) \ No newline at end of file
diff --git a/model-optimizer/mo/front/kaldi/extractors/add_shift_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/add_shift_ext_test.py
new file mode 100644
index 000000000..08703d2a9
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/add_shift_ext_test.py
@@ -0,0 +1,56 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from mo.front.kaldi.extractors.add_shift_ext import AddShiftFrontExtractor
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.front.kaldi.loader.utils_test import TestKaldiUtilsLoading
+from mo.ops.op import Op
+from mo.ops.scale_shift import ScaleShiftOp
+
+
+class AddShiftFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['ScaleShift'] = ScaleShiftOp
+
+ @classmethod
+ def create_pb_for_test_node(cls):
+ input_shape = cls.test_node.in_node().shape
+ pb = cls.write_tag_with_value('<LearnRateCoef>', 0)
+ pb += cls.write_tag_with_value('FV', input_shape[1])
+ for i in np.zeros(input_shape[1], dtype=np.uint32):
+ pb += TestKaldiUtilsLoading.pack_value(i, TestKaldiUtilsLoading.uint32_fmt)
+ cls.test_node['parameters'] = TestKaldiUtilsLoading.bytesio_from(pb)
+ AddShiftFrontExtractor.extract(cls.test_node)
+
+ def test_assertion(self):
+ self.assertRaises(AttributeError, AddShiftFrontExtractor.extract, None)
+
+ def test_extracted_shapes_add_shift(self):
+ weights = self.test_node.weights
+ biases = self.test_node.biases
+ weights_shape = weights.shape[0]
+ self.assertEqual(self.test_node.in_node().shape[1], weights_shape)
+ self.assertEqual(biases.shape[0], weights_shape)
+
+ def test_extracted_blobs_add_shift(self):
+ weights = self.test_node.weights
+ biases = self.test_node.biases
+ self.assertTrue(np.array_equal(weights, np.ones(weights.shape)))
+ self.assertTrue(np.array_equal(biases, np.zeros(biases.shape)))
+ self.assertTrue(self.test_node.bias_term)
diff --git a/model-optimizer/mo/front/kaldi/extractors/affine_component_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/affine_component_ext_test.py
new file mode 100644
index 000000000..14b083b7f
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/affine_component_ext_test.py
@@ -0,0 +1,48 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from mo.front.kaldi.extractors.affine_transform_ext import AffineTransformFrontExtractor
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.front.kaldi.loader.utils_test import TestKaldiUtilsLoading
+from mo.ops.inner_product import InnerProduct
+from mo.ops.op import Op
+
+
+class AffineComponentFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['FullyConnected'] = InnerProduct
+
+ @classmethod
+ def create_pb_for_test_node(cls):
+ pb = KaldiFrontExtractorTest.generate_learn_info()
+ pb += KaldiFrontExtractorTest.generate_matrix([10, 10])
+ pb += KaldiFrontExtractorTest.generate_vector(10)
+ cls.test_node['parameters'] = TestKaldiUtilsLoading.bytesio_from(pb)
+ AffineTransformFrontExtractor.extract(cls.test_node)
+
+ def test_assertion(self):
+ self.assertRaises(AttributeError, AffineTransformFrontExtractor.extract, None)
+
+ def test_attrs(self):
+ self.assertEqual(self.test_node['out-size'], 10)
+ self.assertEqual(self.test_node['layout'], 'NCHW')
+
+ def test_out_blobs(self):
+ self.assertTrue(np.array_equal(self.test_node.weights, range(10 * 10)))
+ self.assertTrue(np.array_equal(self.test_node.biases, range(10)))
diff --git a/model-optimizer/mo/front/kaldi/extractors/affine_transform_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/affine_transform_ext_test.py
new file mode 100644
index 000000000..7b9f41c37
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/affine_transform_ext_test.py
@@ -0,0 +1,52 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import types
+
+import numpy as np
+
+from mo.front.kaldi.extractors.affine_transform_ext import AffineTransformFrontExtractor
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.front.kaldi.loader.utils_test import TestKaldiUtilsLoading
+from mo.ops.inner_product import InnerProduct
+from mo.ops.op import Op
+
+
+class AffineTransformFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['FullyConnected'] = InnerProduct
+
+ @classmethod
+ def create_pb_for_test_node(cls):
+ pb = KaldiFrontExtractorTest.generate_learn_info()
+ pb += KaldiFrontExtractorTest.generate_matrix([10, 10])
+ pb += KaldiFrontExtractorTest.generate_vector(10)
+ cls.test_node['parameters'] = TestKaldiUtilsLoading.bytesio_from(pb)
+ AffineTransformFrontExtractor.extract(cls.test_node)
+
+ def test_assertion(self):
+ self.assertRaises(AttributeError, AffineTransformFrontExtractor.extract, None)
+
+ def test_attrs(self):
+ self.assertEqual(self.test_node['out-size'], 10)
+ self.assertEqual(self.test_node['layout'], 'NCHW')
+
+ def test_out_blobs(self):
+ self.assertTrue(np.array_equal(self.test_node.weights, range(10 * 10)))
+ self.assertTrue(np.array_equal(self.test_node.biases, range(10)))
+
+
diff --git a/model-optimizer/mo/front/kaldi/extractors/common_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/common_ext_test.py
new file mode 100644
index 000000000..e9cdb98c9
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/common_ext_test.py
@@ -0,0 +1,113 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import networkx as nx
+import numpy as np
+
+from mo.front.common.partial_infer.utils import int64_array
+from mo.front.kaldi.loader.utils_test import TestKaldiUtilsLoading
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+
+class KaldiFrontExtractorTest(unittest.TestCase):
+ graph = nx.MultiDiGraph()
+
+ @classmethod
+ def setUp(cls):
+ cls.nodes_attributes = {
+ 'input_data_node': {
+ 'name': 'input_data_node',
+ 'kind': 'data',
+ 'shape': np.array([1, 32, 1, 40], dtype=np.int64),
+ },
+ 'weights': {
+ 'name': 'weights',
+ 'kind': 'data',
+ 'shape': np.array([10, 32, 1, 8], dtype=np.int64),
+ 'value': np.zeros((10, 32, 1, 8)),
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis'],
+ },
+ 'test_node': {
+ 'name': 'test_node',
+ 'kind': 'op'
+ },
+ 'output_data_node': {
+ 'name': 'output_data_node',
+ 'kind': 'data',
+ 'shape': None
+ }
+ }
+ cls.create_graph()
+ cls.test_node = Node(cls.graph, 'test_node')
+ cls.graph.add_node(cls.test_node.id, type='test_node')
+ cls.register_op()
+ cls.create_pb_for_test_node()
+
+ @staticmethod
+ def register_op():
+ raise NotImplementedError('Please, implement register_op')
+
+ @classmethod
+ def create_graph(cls):
+ cls.graph = build_graph(cls.nodes_attributes, [
+ ('input_data_node', 'test_node'),
+ ('test_node', 'output_data_node')
+ ], nodes_with_edges_only=True)
+
+ @classmethod
+ def create_pb_for_test_node(cls):
+ pass
+
+ @staticmethod
+ def generate_learn_info():
+ pb = KaldiFrontExtractorTest.write_tag_with_value('<LearnRateCoef>', 0)
+ pb += KaldiFrontExtractorTest.write_tag_with_value('<BiasLearnRateCoef>', 1)
+ pb += KaldiFrontExtractorTest.write_tag_with_value('<MaxNorm>', 2)
+ return pb
+
+ @staticmethod
+ def generate_matrix(shape):
+ pb = KaldiFrontExtractorTest.write_tag_with_value('FM', shape[0])
+ pb += KaldiFrontExtractorTest.write_int_value(shape[1])
+ pb += KaldiFrontExtractorTest.generate_blob(np.prod(shape))
+ return pb
+
+ @staticmethod
+ def generate_vector(size: int) -> bytes:
+ pb = KaldiFrontExtractorTest.write_tag_with_value('FV', size)
+ pb += KaldiFrontExtractorTest.generate_blob(size)
+ return pb
+
+ @staticmethod
+ def generate_blob(size: int) -> bytes:
+ pb = b''
+ for i in range(size):
+ pb += TestKaldiUtilsLoading.pack_value(i, TestKaldiUtilsLoading.float32_fmt)
+ return pb
+
+ @staticmethod
+ def write_tag_with_value(tag: str, value) -> bytes:
+ pb = bytes(tag + ' ', 'ascii')
+ return pb + KaldiFrontExtractorTest.write_int_value(value)
+
+ @staticmethod
+ def write_int_value(value) -> bytes:
+ pb = TestKaldiUtilsLoading.pack_value(4, 'B')
+ pb += TestKaldiUtilsLoading.pack_value(value, TestKaldiUtilsLoading.uint32_fmt)
+ return pb
diff --git a/model-optimizer/mo/front/kaldi/extractors/concat_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/concat_ext_test.py
new file mode 100644
index 000000000..b2274badf
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/concat_ext_test.py
@@ -0,0 +1,30 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.front.kaldi.extractors.concat_ext import ConcatFrontExtractor
+from mo.ops.convolution import Convolution
+from mo.ops.op import Op
+
+
+class ConcatFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['Concat'] = Convolution
+
+ def test_concat(self):
+ ConcatFrontExtractor.extract(self.test_node)
+ self.assertEqual(self.test_node.axis, 1)
diff --git a/model-optimizer/mo/front/kaldi/extractors/convolutional_component_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/convolutional_component_ext_test.py
new file mode 100644
index 000000000..50fef84f8
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/convolutional_component_ext_test.py
@@ -0,0 +1,67 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import numpy as np
+
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.front.kaldi.extractors.convolutional_component_ext import ConvolutionalComponentFrontExtractor
+from mo.front.kaldi.loader.utils_test import TestKaldiUtilsLoading
+from mo.ops.convolution import Convolution
+from mo.ops.op import Op
+
+
+class ConvolutionalComponentFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['Convolution'] = Convolution
+
+ @classmethod
+ def create_pb_for_test_node(cls):
+ pb = KaldiFrontExtractorTest.write_tag_with_value('<PatchDim>', 2)
+ pb += KaldiFrontExtractorTest.write_tag_with_value('<PatchStep>', 2)
+ pb += KaldiFrontExtractorTest.write_tag_with_value('<PatchStride>', 4)
+ pb += KaldiFrontExtractorTest.generate_learn_info()
+ pb += b'<Filters> '
+ pb += KaldiFrontExtractorTest.generate_matrix([2, 1])
+ pb += b'<Bias> '
+ pb += KaldiFrontExtractorTest.generate_vector(2)
+ cls.test_node['parameters'] = TestKaldiUtilsLoading.bytesio_from(pb)
+ ConvolutionalComponentFrontExtractor.extract(cls.test_node)
+
+ def test_assertion(self):
+ self.assertRaises(AttributeError, ConvolutionalComponentFrontExtractor.extract, None)
+
+ def test_attrs(self):
+ val_attrs = {
+ 'kernel': [1, 1, 1, 2],
+ 'stride': [1, 1, 1, 2],
+ 'pad': [[[0, 0], [0, 0], [0, 0], [0, 0]]],
+ 'output': 2,
+ 'patch_stride': 4,
+ 'spatial_dims': [2, 3],
+ 'channel_dims': [1],
+ 'batch_dims': [0],
+ 'dilation': [1, 1, 1, 1]
+ }
+ for attr in val_attrs:
+ if isinstance(val_attrs[attr], list):
+ self.assertTrue((self.test_node[attr] == val_attrs[attr]).all())
+ else:
+ self.assertEqual(self.test_node[attr], val_attrs[attr])
+
+ def test_convolution_blobs(self):
+ self.assertTrue(np.array_equal(self.test_node.weights, [0, 1]))
+ self.assertTrue(np.array_equal(self.test_node.biases, [0, 1]))
+
diff --git a/model-optimizer/mo/front/kaldi/extractors/fixed_affine_component_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/fixed_affine_component_ext_test.py
new file mode 100644
index 000000000..e03f6982d
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/fixed_affine_component_ext_test.py
@@ -0,0 +1,49 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.front.kaldi.extractors.fixed_affine_component_ext import FixedAffineComponentFrontExtractor
+from mo.front.kaldi.loader.utils_test import TestKaldiUtilsLoading
+from mo.ops.inner_product import InnerProduct
+from mo.ops.op import Op
+
+
+class FixedAffineComponentFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['FullyConnected'] = InnerProduct
+
+ @classmethod
+ def create_pb_for_test_node(cls):
+ pb = b'<LinearParams> ' + KaldiFrontExtractorTest.generate_matrix([10, 10])
+ pb += b'<BiasParams> ' + KaldiFrontExtractorTest.generate_vector(10)
+ cls.test_node['parameters'] = TestKaldiUtilsLoading.bytesio_from(pb)
+ FixedAffineComponentFrontExtractor.extract(cls.test_node)
+
+ def test_assertion(self):
+ self.assertRaises(AttributeError, FixedAffineComponentFrontExtractor.extract, None)
+
+ def test_attrs(self):
+ self.assertEqual(self.test_node['out-size'], 10)
+ self.assertEqual(self.test_node['layout'], 'NCHW')
+
+ def test_out_blobs(self):
+ self.assertTrue(np.array_equal(self.test_node.weights, range(10 * 10)))
+ self.assertTrue(np.array_equal(self.test_node.biases, range(10)))
+
+
diff --git a/model-optimizer/mo/front/kaldi/extractors/max_pooling_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/max_pooling_ext_test.py
new file mode 100644
index 000000000..b3e7ad161
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/max_pooling_ext_test.py
@@ -0,0 +1,52 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+
+from mo.front.kaldi.extractors.max_pooling_ext import MaxPoolingComponentFrontExtractor
+from mo.front.kaldi.loader.utils_test import TestKaldiUtilsLoading
+from mo.ops.op import Op
+from mo.ops.pooling import Pooling
+
+
+class MaxPoolingComponentFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['Pooling'] = Pooling
+
+ @classmethod
+ def create_pb_for_test_node(cls):
+ pb = KaldiFrontExtractorTest.write_tag_with_value('<PoolSize>', 2)
+ pb += KaldiFrontExtractorTest.write_tag_with_value('<PoolStep>', 2)
+ pb += KaldiFrontExtractorTest.write_tag_with_value('<PoolStride>', 4)
+ cls.test_node['parameters'] = TestKaldiUtilsLoading.bytesio_from(pb)
+ MaxPoolingComponentFrontExtractor.extract(cls.test_node)
+
+ def test_assertion(self):
+ self.assertRaises(AttributeError, MaxPoolingComponentFrontExtractor.extract, None)
+
+ def test_attrs(self):
+ val_attrs = {
+ 'window': [1, 1, 1, 2],
+ 'stride': [1, 1, 2, 2],
+ 'pool_stride': 4,
+ 'pad': [[[0, 0], [0, 0], [0, 0], [0, 0]]]
+ }
+ for attr in val_attrs:
+ if isinstance(val_attrs[attr], list):
+ self.assertTrue((self.test_node[attr] == val_attrs[attr]).all())
+ else:
+ self.assertEqual(self.test_node[attr], val_attrs[attr])
diff --git a/model-optimizer/mo/front/kaldi/extractors/rescale_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/rescale_ext_test.py
new file mode 100644
index 000000000..b7628bb24
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/rescale_ext_test.py
@@ -0,0 +1,51 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.front.kaldi.extractors.rescale_ext import RescaleFrontExtractor
+from mo.front.kaldi.loader.utils_test import TestKaldiUtilsLoading
+from mo.ops.op import Op
+from mo.ops.scale_shift import ScaleShiftOp
+
+
+class RescaleFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['ScaleShift'] = ScaleShiftOp
+
+ @classmethod
+ def create_pb_for_test_node(cls):
+ input_shape = cls.test_node.in_node().shape
+ pb = cls.write_tag_with_value('<LearnRateCoef>', 0)
+ pb += cls.write_tag_with_value('FV', input_shape[1])
+ for i in range(input_shape[1]):
+ pb += TestKaldiUtilsLoading.pack_value(i, TestKaldiUtilsLoading.float32_fmt)
+ cls.test_node['parameters'] = TestKaldiUtilsLoading.bytesio_from(pb)
+ RescaleFrontExtractor.extract(cls.test_node)
+
+ def test_assertion(self):
+ self.assertRaises(AttributeError, RescaleFrontExtractor.extract, None)
+
+ def test_extracted_shapes_add_shift(self):
+ weights = self.test_node.weights
+ weights_shape = weights.shape[0]
+ self.assertEqual(self.test_node.in_node().shape[1], weights_shape)
+
+ def test_extracted_blobs_add_shift(self):
+ weights = self.test_node.weights
+ self.assertTrue(np.array_equal(weights, range(self.test_node.in_node().shape[1])))
diff --git a/model-optimizer/mo/front/kaldi/extractors/sigmoid_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/sigmoid_ext_test.py
new file mode 100644
index 000000000..521ac062f
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/sigmoid_ext_test.py
@@ -0,0 +1,33 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from mo.front.kaldi.extractors.sigmoid_ext import SigmoidFrontExtractor
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.ops.activation import Activation
+from mo.ops.op import Op
+
+
+class SigmoidFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['Activation'] = Activation
+
+ def test_assertion(self):
+ self.assertRaises(AttributeError, SigmoidFrontExtractor.extract, None)
+
+ def test_extracted_blobs_add_shift(self):
+ SigmoidFrontExtractor.extract(self.test_node)
+ self.assertTrue(self.test_node.operation, 'sigmoid')
diff --git a/model-optimizer/mo/front/kaldi/extractors/slice_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/slice_ext_test.py
new file mode 100644
index 000000000..0c2a16c3c
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/slice_ext_test.py
@@ -0,0 +1,35 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.front.kaldi.extractors.slice_ext import SliceFrontExtractor
+from mo.ops.op import Op
+from mo.ops.slice import Slice
+from mo.utils.unittest.extractors import FakeMultiParam
+
+
+class SliceFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['Slice'] = Slice
+ cls.slice_params = {
+ 'slice_point': [99, 1320],
+ 'axis': 1
+ }
+ cls.test_node['pb'] = FakeMultiParam(cls.slice_params)
+
+ def test_assertion_no_pb(self):
+ self.assertRaises(AttributeError, SliceFrontExtractor.extract, None)
diff --git a/model-optimizer/mo/front/kaldi/extractors/tanh_ext_test.py b/model-optimizer/mo/front/kaldi/extractors/tanh_ext_test.py
new file mode 100644
index 000000000..4604022b8
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/extractors/tanh_ext_test.py
@@ -0,0 +1,33 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from mo.front.kaldi.extractors.common_ext_test import KaldiFrontExtractorTest
+from mo.front.kaldi.extractors.tanh_component_ext import TanhFrontExtractor
+from mo.ops.activation import Activation
+from mo.ops.op import Op
+
+
+class TanhFrontExtractorTest(KaldiFrontExtractorTest):
+ @classmethod
+ def register_op(cls):
+ Op.registered_ops['Activation'] = Activation
+
+ def test_assertion(self):
+ self.assertRaises(AttributeError, TanhFrontExtractor.extract, None)
+
+ def test_extracted_blobs_add_shift(self):
+ TanhFrontExtractor.extract(self.test_node)
+ self.assertTrue(self.test_node.operation, 'sigmoid')
diff --git a/model-optimizer/mo/front/kaldi/loader/utils_test.py b/model-optimizer/mo/front/kaldi/loader/utils_test.py
new file mode 100644
index 000000000..ba5b06bb6
--- /dev/null
+++ b/model-optimizer/mo/front/kaldi/loader/utils_test.py
@@ -0,0 +1,97 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import io
+import struct
+import unittest
+
+from mo.front.kaldi.loader.utils import end_of_nnet_tag, end_of_component_tag, get_bool, get_uint16, get_uint32, \
+ get_uint64, read_binary_bool_token, read_binary_integer32_token, read_binary_integer64_token, find_next_tag, \
+ find_next_component, find_end_of_component, get_parameters
+from mo.utils.error import Error
+
+
+class TestKaldiUtilsLoading(unittest.TestCase):
+ bool_fmt = '?'
+ uint16_fmt = 'H'
+ uint32_fmt = 'I'
+ uint64_fmt = 'q'
+ float32_fmt = 'f'
+
+ @staticmethod
+ def bytesio_from(buffer):
+ return io.BytesIO(buffer)
+
+ @staticmethod
+ def pack_value(value, fmt):
+ return struct.pack(fmt, value)
+
+ def test_check_common_tags(self):
+ self.assertEqual(end_of_nnet_tag, '</Nnet>')
+ self.assertEqual(end_of_component_tag, '<!EndOfComponent>')
+
+ def test_check_results_getting_function(self):
+ self.assertTrue(get_bool(self.pack_value(True, self.bool_fmt)))
+ self.assertFalse(get_bool(self.pack_value(False, self.bool_fmt)))
+ self.assertEqual(get_uint16(self.pack_value(16, self.uint16_fmt)), 16)
+ self.assertEqual(get_uint32(self.pack_value(32, self.uint32_fmt)), 32)
+ self.assertEqual(get_uint64(self.pack_value(64, self.uint64_fmt)), 64)
+
+ def test_read_binary_bool_token(self):
+ true_value = self.bytesio_from(self.pack_value(True, self.bool_fmt))
+ false_value = self.bytesio_from(self.pack_value(False, self.bool_fmt))
+ self.assertTrue(read_binary_bool_token(true_value))
+ self.assertFalse(read_binary_bool_token(false_value))
+
+ def test_read_binary_integer32_token(self):
+ stream = self.bytesio_from(self.pack_value(4, 'B') + self.pack_value(32, self.uint32_fmt))
+ self.assertEqual(read_binary_integer32_token(stream), 32)
+
+ def test_read_binary_integer64_token(self):
+ stream = self.bytesio_from(self.pack_value(8, 'B') + self.pack_value(64, self.uint64_fmt))
+ self.assertEqual(read_binary_integer64_token(stream), 64)
+
+ def test_find_next_tag(self):
+ test_token = b'<TestToken>'
+ self.assertEqual(find_next_tag(self.bytesio_from(test_token)), test_token.decode('ascii'))
+ fake_token = b'<FakeBegin' + test_token
+ self.assertEqual(find_next_tag(self.bytesio_from(fake_token)), test_token.decode('ascii'))
+
+ def test_find_next_tag_raise_error(self):
+ test_token = b'some bytes'
+ self.assertRaises(Error, find_next_tag, self.bytesio_from(test_token))
+
+ def test_find_next_component(self):
+ component = b'<LstmProjectedStreams>'
+ test_file = b'<Nnet>somefakeinfo<another>info' + component + b'<tag><!EndOfComponent></Nnet>'
+ self.assertEqual(find_next_component(self.bytesio_from(test_file)), component.decode('ascii').lower()[1:-1])
+
+ def test_find_next_component_end_of_nnet(self):
+ test_file = b'<Nnet>somefakeinfo<another>info<tag><!EndOfComponent></Nnet>'
+ self.assertEqual(find_next_component(self.bytesio_from(test_file)), end_of_nnet_tag.lower()[1:-1])
+
+ def test_find_end_of_component(self):
+ component = '<AffineComponent>'
+ test_file = b'somefakeinfo<another>info<tag>' + bytes(end_of_component_tag, 'ascii') + b'</Nnet>'
+ end_tag, position = find_end_of_component(self.bytesio_from(test_file), component.lower()[1:-1])
+ self.assertEqual(end_tag, end_of_component_tag)
+ self.assertEqual(position, test_file.decode('ascii').index(end_of_component_tag) + len(end_of_component_tag))
+
+ def test_get_pb(self):
+ component = '<AffineComponent>'
+ test_file = b'somefakeinfo<another>info<tag>' + bytes(end_of_component_tag, 'ascii') + b'</Nnet>'
+ end_tag, end_position = find_end_of_component(self.bytesio_from(test_file), component[1:-1].lower())
+ pb = get_parameters(self.bytesio_from(test_file), 0, end_position)
diff --git a/model-optimizer/mo/front/mxnet/extractors/activation_test.py b/model-optimizer/mo/front/mxnet/extractors/activation_test.py
new file mode 100644
index 000000000..d7e034ce8
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/activation_test.py
@@ -0,0 +1,63 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.mxnet.extractors.activation import ActivationFrontExtractor
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+
+class TestActivationFrontExtractorOp(unittest.TestCase):
+ def test_extract_sigmoid_layer(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'act_node': {'type': 'Activation', 'kind': 'op', 'op': 'Activation', },
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ },
+ [
+ ('node_1', 'act_node'),
+ ('act_node', 'node_2'),
+ ],
+ {
+ 'act_node': {'symbol_dict': {'attrs': {'act_type': 'sigmoid'}}},
+ })
+
+ act_node = Node(graph, 'act_node')
+ act_extr_op = ActivationFrontExtractor()
+ supported = act_extr_op.extract(act_node)
+ self.assertTrue(supported)
+ self.assertEqual(act_node['op'], 'Activation')
+
+ def test_extract_relu_layer(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'act_node': {'type': 'relu', 'kind': 'op', 'op': 'Activation', },
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ },
+ [
+ ('node_1', 'act_node'),
+ ('act_node', 'node_2'),
+ ],
+ {
+ 'act_node': {'symbol_dict': {'attrs': {'act_type': 'relu'}}},
+ })
+
+ act_node = Node(graph, 'act_node')
+ act_extr_op = ActivationFrontExtractor()
+ supported = act_extr_op.extract(act_node)
+ self.assertTrue(supported)
+ self.assertEqual(act_node['op'], 'ReLU')
diff --git a/model-optimizer/mo/front/mxnet/extractors/crop_test.py b/model-optimizer/mo/front/mxnet/extractors/crop_test.py
new file mode 100644
index 000000000..06b839c08
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/crop_test.py
@@ -0,0 +1,44 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.common.partial_infer.crop import crop_infer
+from mo.front.mxnet.extractors.crop import crop_ext
+from mo.front.mxnet.extractors.utils import AttrDictionary
+
+
+class FakeProtoLayer:
+ def __init__(self, val):
+ self.crop_param = val
+
+
+class TestCropExt(unittest.TestCase):
+ def test_crop_ext(self):
+ params = {
+ 'offset': '(5, 5)',
+ 'num_args': 2
+ }
+ res = crop_ext(AttrDictionary(params))
+ exp_res = {
+ 'axis': 2,
+ 'offset': [5, 5],
+ 'dim': None,
+ 'infer': crop_infer,
+ 'type': 'Crop'
+ }
+ for key in exp_res.keys():
+ self.assertEqual(res[key], exp_res[key])
diff --git a/model-optimizer/mo/front/mxnet/extractors/eltwise_test.py b/model-optimizer/mo/front/mxnet/extractors/eltwise_test.py
new file mode 100644
index 000000000..4d07e57d0
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/eltwise_test.py
@@ -0,0 +1,44 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.mxnet.extractors.eltwise import eltwise_ext
+from mo.front.mxnet.extractors.utils import AttrDictionary
+
+
+class TestEltwiseParsing(unittest.TestCase):
+ def test_eltwise_sum(self):
+ attrs = {}
+ res = eltwise_ext(AttrDictionary(attrs), infer=lambda a, b: a + b, op_type="sum")
+ exp_attrs = {
+ 'type': 'Eltwise',
+ 'operation': 'sum'
+ }
+
+ for key in exp_attrs.keys():
+ self.assertEqual(res[key], exp_attrs[key])
+
+ def test_eltwise_mul(self):
+ attrs = {}
+ res = eltwise_ext(AttrDictionary(attrs), infer=lambda a, b: a * b, op_type="mul")
+ exp_attrs = {
+ 'type': 'Eltwise',
+ 'operation': 'mul'
+ }
+
+ for key in exp_attrs.keys():
+ self.assertEqual(res[key], exp_attrs[key])
diff --git a/model-optimizer/mo/front/mxnet/extractors/leaky_relu_test.py b/model-optimizer/mo/front/mxnet/extractors/leaky_relu_test.py
new file mode 100644
index 000000000..f3fab2b18
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/leaky_relu_test.py
@@ -0,0 +1,89 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.mxnet.extractors.leaky_relu import LeakyReLUFrontExtractor
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+
+class TestLeakyReLUFrontExtractorOp(unittest.TestCase):
+ def test_extract_leaky_relu_layer(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'act_node': {'type': 'LeakyReLU', 'kind': 'op', 'op': 'LeakyReLU', },
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ },
+ [
+ ('node_1', 'act_node'),
+ ('act_node', 'node_2'),
+ ],
+ {
+ 'act_node': {'symbol_dict': {'attrs': {'slope': '0.6'}}},
+ })
+
+ act_node = Node(graph, 'act_node')
+ act_extr_op = LeakyReLUFrontExtractor()
+ supported = act_extr_op.extract(act_node)
+ self.assertTrue(supported)
+ self.assertEqual(act_node['op'], 'ReLU')
+ self.assertEqual(act_node['negative_slope'], 0.6)
+
+ def test_extract_prelu_layer(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'node_3': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'act_node': {'type': 'LeakyReLU', 'kind': 'op', 'op': 'LeakyReLU', },
+ 'node_2': {'type': 'Identity', 'kind': 'op'},
+ },
+ [
+ ('node_1', 'act_node'),
+ ('node_3', 'act_node'),
+ ('act_node', 'node_2'),
+ ],
+ {
+ 'act_node': {'symbol_dict': {'attrs': {'act_type': 'prelu'}}},
+ 'node_3': {'value': np.array([1], dtype=np.float32)},
+ })
+ act_node = Node(graph, 'act_node')
+ act_extr_op = LeakyReLUFrontExtractor()
+ supported = act_extr_op.extract(act_node)
+ self.assertTrue(supported)
+ self.assertEqual(act_node['op'], 'PReLU')
+
+ def test_extract_elu_layer(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'act_node': {'type': 'LeakyReLU', 'kind': 'op', 'op': 'LeakyReLU', },
+ 'node_2': {'type': 'Placeholder', 'kind': 'op'},
+ },
+ [
+ ('node_1', 'act_node'),
+ ('act_node', 'node_2'),
+ ],
+ {
+ 'act_node': {'symbol_dict': {'attrs': {'act_type': 'elu'}}},
+ })
+
+ act_node = Node(graph, 'act_node')
+ act_extr_op = LeakyReLUFrontExtractor()
+ supported = act_extr_op.extract(act_node)
+ self.assertTrue(supported)
+ self.assertEqual(act_node['op'], 'Activation')
+ self.assertEqual(act_node['operation'], 'elu')
diff --git a/model-optimizer/mo/front/mxnet/extractors/multibox_detection_test.py b/model-optimizer/mo/front/mxnet/extractors/multibox_detection_test.py
new file mode 100644
index 000000000..c6e4c0c07
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/multibox_detection_test.py
@@ -0,0 +1,80 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.mxnet.extractors.multibox_detection import multi_box_detection_ext
+from mo.front.mxnet.extractors.utils import AttrDictionary
+
+
+class TestMultiBoxDetection_Parsing(unittest.TestCase):
+ def test_multi_box_detection_check_attrs(self):
+ attrs = {
+ "force_suppress": "True",
+ "nms_threshold": "0.4",
+ "nms_topk": "400",
+ "variances": "(0.1, 0.1, 0.2, 0.2)"
+ }
+
+ res = multi_box_detection_ext(AttrDictionary(attrs))
+
+ exp_attrs = {
+ 'type': 'DetectionOutput',
+ 'num_classes': 21,
+ 'keep_top_k': 400,
+ 'variance_encoded_in_target': 0,
+ 'code_type': "caffe.PriorBoxParameter.CENTER_SIZE",
+ 'share_location': 1,
+ 'confidence_threshold': 0.01,
+ 'background_label_id': 0,
+ 'nms_threshold': 0.4,
+ 'top_k': 400,
+ 'decrease_label_id': 1,
+ 'clip': 1,
+ 'normalized': 1,
+ }
+
+ for key in exp_attrs.keys():
+ self.assertEqual(res[key], exp_attrs[key])
+
+ def test_multi_box_detection_check_attrs_without_top_k(self):
+ attrs = {
+ "force_suppress": "True",
+ "nms_threshold": "0.2",
+ "threshold": "0.02",
+ "variances": "(0.1, 0.1, 0.2, 0.2)"
+ }
+
+ res = multi_box_detection_ext(AttrDictionary(attrs))
+
+ exp_attrs = {
+ 'type': 'DetectionOutput',
+ 'num_classes': 21,
+ 'keep_top_k': -1,
+ 'variance_encoded_in_target': 0,
+ 'code_type': "caffe.PriorBoxParameter.CENTER_SIZE",
+ 'share_location': 1,
+ 'confidence_threshold': 0.02,
+ 'background_label_id': 0,
+ 'nms_threshold': 0.2,
+ 'top_k': -1,
+ 'decrease_label_id': 1,
+ 'clip': 1,
+ 'normalized': 1,
+ }
+
+ for key in exp_attrs.keys():
+ self.assertEqual(res[key], exp_attrs[key])
diff --git a/model-optimizer/mo/front/mxnet/extractors/multibox_prior_test.py b/model-optimizer/mo/front/mxnet/extractors/multibox_prior_test.py
new file mode 100644
index 000000000..cc2cc8f66
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/multibox_prior_test.py
@@ -0,0 +1,56 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.mxnet.extractors.multibox_prior import multi_box_prior_ext
+from mo.front.mxnet.extractors.utils import AttrDictionary
+
+
+class TestMultiBoxPrior_Parsing(unittest.TestCase):
+ def test_multi_box_prior_check_attrs(self):
+ attrs = {
+ 'ratios': '(1,2,0.5)',
+ 'steps': '(0.02666666666666667, 0.02666666666666667)',
+ 'clip': 'False',
+ 'sizes': '(0.1,0.141)'
+ }
+
+ res = multi_box_prior_ext(AttrDictionary(attrs))
+ exp_attrs = {
+ 'type': 'PriorBox',
+ 'img_size': 0,
+ 'img_h': 0,
+ 'img_w': 0,
+ 'step': 0.02666666666666667,
+ 'step_h': 0,
+ 'step_w': 0,
+ 'offset': 0.5,
+ 'variance': '0.100000,0.100000,0.200000,0.200000',
+ 'flip': 0,
+ 'clip': 0,
+ 'min_size': (0.1, 0.141),
+ 'max_size': '',
+ 'aspect_ratio': [1, 2, 0.5],
+ }
+
+ for key in exp_attrs.keys():
+ if key in ['aspect_ratio', 'variance']:
+ np.testing.assert_equal(res[key], exp_attrs[key])
+ else:
+ self.assertEqual(res[key], exp_attrs[key])
diff --git a/model-optimizer/mo/front/mxnet/extractors/relu_test.py b/model-optimizer/mo/front/mxnet/extractors/relu_test.py
new file mode 100644
index 000000000..c045d869b
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/relu_test.py
@@ -0,0 +1,43 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.mxnet.extractors.relu import ReLUFrontExtractor
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+
+class TestReluFrontExtractorOp(unittest.TestCase):
+ def test_extract_relu_layer(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'relu_node': {'type': 'relu', 'kind': 'op', 'op': 'relu', },
+ 'node_2': {'type': 'Placeholder', 'kind': 'op'},
+ },
+ [
+ ('node_1', 'relu_node'),
+ ('relu_node', 'node_2'),
+ ],
+ {
+ 'relu_node': {'symbol_dict': {'attrs': {}}},
+ })
+
+ relu_node = Node(graph, 'relu_node')
+ relu_extr_op = ReLUFrontExtractor()
+ supported = relu_extr_op.extract(relu_node)
+ self.assertTrue(supported)
+ self.assertEqual(relu_node['op'], 'ReLU')
diff --git a/model-optimizer/mo/front/mxnet/extractors/sigmoid_test.py b/model-optimizer/mo/front/mxnet/extractors/sigmoid_test.py
new file mode 100644
index 000000000..fcf5893a4
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/sigmoid_test.py
@@ -0,0 +1,44 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.mxnet.extractors.sigmoid import Sigmoid
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+
+class TestSigmoidFrontExtractorOp(unittest.TestCase):
+ def test_extract_sigmoid_layer(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'sigmoid_node': {'type': 'sigmoid', 'kind': 'op', 'op': 'sigmoid', },
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [
+ ('node_1', 'sigmoid_node'),
+ ('sigmoid_node', 'node_3'),
+ ],
+ {
+ 'sigmoid_node': {'symbol_dict': {'attrs': {}}},
+ })
+
+ sigmoid_node = Node(graph, 'sigmoid_node')
+ sigmoid_extr_op = Sigmoid()
+ supported = sigmoid_extr_op.extract(sigmoid_node)
+ self.assertTrue(supported)
+ self.assertEqual(sigmoid_node['op'], 'Activation')
+ self.assertEqual(sigmoid_node['operation'], 'sigmoid')
diff --git a/model-optimizer/mo/front/mxnet/extractors/slice_axis_test.py b/model-optimizer/mo/front/mxnet/extractors/slice_axis_test.py
new file mode 100644
index 000000000..435044d10
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/slice_axis_test.py
@@ -0,0 +1,51 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.mxnet.extractors.slice_axis import slice_axis_ext
+from mo.front.mxnet.extractors.utils import AttrDictionary
+from mo.graph.graph import Node
+from mo.utils.unittest.graph import build_graph
+
+
+class TestMXNetSliceAxisExtractorOp(unittest.TestCase):
+ def test_extract_slice_axis_layer(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'slice_axis_node': {'type': 'sigmoid', 'kind': 'op', 'op': 'slice_axis', },
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [
+ ('node_1', 'slice_axis_node'),
+ ('slice_axis_node', 'node_3'),
+ ],
+ {
+ 'slice_axis_node': {'symbol_dict': {'attrs': {'axis': 0, 'begin': 10, 'end': 25}}},
+ })
+
+ exp_attrs = {
+ 'type': 'Crop',
+ 'axis': 0,
+ 'offset': 10,
+ 'dim': 25
+ }
+
+ slice_axis_node = Node(graph, 'slice_axis_node')
+ res = slice_axis_ext(AttrDictionary(slice_axis_node['symbol_dict']['attrs']))
+
+ for key in exp_attrs.keys():
+ self.assertEqual(res[key], exp_attrs[key])
diff --git a/model-optimizer/mo/front/mxnet/extractors/utils_test.py b/model-optimizer/mo/front/mxnet/extractors/utils_test.py
new file mode 100644
index 000000000..070d53231
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/extractors/utils_test.py
@@ -0,0 +1,202 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+import mxnet as mx
+
+from mo.front.mxnet.extractors.utils import AttrDictionary
+from mo.front.mxnet.extractors.utils import load_params
+
+
+class TestAttrDictionary(unittest.TestCase):
+ def testBool(self):
+ attrs = {
+ "global_pool": "True"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ global_pool = attr_dict.bool("global_pool", False)
+ self.assertEqual(True, global_pool)
+
+ def testBoolAsDigits(self):
+ attrs = {
+ "global_pool": "1"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ global_pool = attr_dict.bool("global_pool", False)
+ self.assertEqual(True, global_pool)
+
+ def testBoolWithoutAttr(self):
+ attrs = {
+ "something": "1"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ global_pool = attr_dict.bool("global_pool", False)
+ self.assertEqual(False, global_pool)
+
+ def testStrAttr(self):
+ attrs = {
+ "something": "Val"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ attr = attr_dict.str("something", "Text")
+ self.assertEqual("Val", attr)
+
+ def testStrAttrWithoutAttr(self):
+ attrs = {
+ "something2": "Val"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ attr = attr_dict.str("something", "Text")
+ self.assertEqual("Text", attr)
+
+ def testFloatAttr(self):
+ attrs = {
+ "something": "0.5"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ attr = attr_dict.float("something", 0.1)
+ self.assertEqual(0.5, attr)
+
+ def testFloatWithoutAttr(self):
+ attrs = {
+ "something2": "0.5"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ attr = attr_dict.float("something", 0.1)
+ self.assertEqual(0.1, attr)
+
+ def testIntAttr(self):
+ attrs = {
+ "something": "5"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ attr = attr_dict.float("something", 1)
+ self.assertEqual(5, attr)
+
+ def testIntWithoutAttr(self):
+ attrs = {
+ "something2": "5"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ attr = attr_dict.float("something", 1)
+ self.assertEqual(1, attr)
+
+ def testTupleAttr(self):
+ attrs = {
+ "something": "(5,6,7)"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ a, b, c = attr_dict.tuple("something", int, (1, 2, 3))
+ self.assertEqual(5, a)
+ self.assertEqual(6, b)
+ self.assertEqual(7, c)
+
+ def testTupleWithoutAttr(self):
+ attrs = {
+ "something2": "(5,6,7)"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ a, b, c = attr_dict.tuple("something", int, (1, 2, 3))
+ self.assertEqual(1, a)
+ self.assertEqual(2, b)
+ self.assertEqual(3, c)
+
+ def testTupleWithEmptyTupleAttr(self):
+ attrs = {
+ "something2": "()"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ a, b = attr_dict.tuple("something", int, (2, 3))
+ self.assertEqual(2, a)
+ self.assertEqual(3, b)
+
+ def testTupleWithEmptyListAttr(self):
+ attrs = {
+ "something2": "[]"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ a, b = attr_dict.tuple("something", int, (2, 3))
+ self.assertEqual(2, a)
+ self.assertEqual(3, b)
+
+ def testListAttr(self):
+ attrs = {
+ "something": "5,6,7"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ l = attr_dict.list("something", int, [1, 2, 3])
+ self.assertEqual(5, l[0])
+ self.assertEqual(6, l[1])
+ self.assertEqual(7, l[2])
+
+ def testListWithoutAttr(self):
+ attrs = {
+ "something2": "5,6,7"
+ }
+
+ attr_dict = AttrDictionary(attrs)
+ l = attr_dict.list("something", int, [1, 2, 3])
+ self.assertEqual(1, l[0])
+ self.assertEqual(2, l[1])
+ self.assertEqual(3, l[2])
+
+
+class TestUtils(unittest.TestCase):
+ @patch('mxnet.nd.load')
+ def test_load_symbol_nodes_from_params(self, mock_nd_load):
+ mock_nd_load.return_value = {'arg:conv0_weight': mx.nd.array([1, 2], dtype='float32'),
+ 'arg:conv1_weight': mx.nd.array([2, 3], dtype='float32'),
+ 'aux:bn_data_mean': mx.nd.array([5, 6], dtype='float32')}
+ model_params = load_params("model.params")
+ self.assertTrue('conv0_weight' in model_params._param_names)
+ self.assertTrue('conv1_weight' in model_params._param_names)
+ self.assertTrue('bn_data_mean' in model_params._aux_names)
+ self.assertEqual([1., 2.], model_params._arg_params['conv0_weight'].asnumpy().tolist())
+ self.assertEqual([2., 3.], model_params._arg_params['conv1_weight'].asnumpy().tolist())
+ self.assertEqual([5., 6.], model_params._aux_params['bn_data_mean'].asnumpy().tolist())
+
+ @patch('mxnet.nd.load')
+ def test_load_symbol_nodes_from_args_nd(self, mock_nd_load):
+ mock_nd_load.return_value = {'conv0_weight': mx.nd.array([1, 2], dtype='float32'),
+ 'conv1_weight': mx.nd.array([2, 3], dtype='float32')}
+ model_params = load_params("args_model.nd", data_names=('data1', 'data2'))
+ self.assertTrue('conv0_weight' in model_params._param_names)
+ self.assertTrue('conv1_weight' in model_params._param_names)
+ self.assertEqual([1., 2.], model_params._arg_params['conv0_weight'].asnumpy().tolist())
+ self.assertEqual([2., 3.], model_params._arg_params['conv1_weight'].asnumpy().tolist())
+
+ @patch('mxnet.nd.load')
+ def test_load_symbol_nodes_from_auxs_nd(self, mock_nd_load):
+ mock_nd_load.return_value = {'bn_data_mean': mx.nd.array([5, 6], dtype='float32')}
+ model_params = load_params("auxs_model.nd")
+ self.assertTrue('bn_data_mean' in model_params._aux_names)
+ self.assertEqual([5., 6.], model_params._aux_params['bn_data_mean'].asnumpy().tolist())
diff --git a/model-optimizer/mo/front/mxnet/loader_test.py b/model-optimizer/mo/front/mxnet/loader_test.py
new file mode 100644
index 000000000..2c77d7e95
--- /dev/null
+++ b/model-optimizer/mo/front/mxnet/loader_test.py
@@ -0,0 +1,62 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+from mo.front.mxnet.loader import load_symbol_nodes, parse_input_model
+
+
+class MockSymbolLoadObj():
+ def tojson(self):
+ pass
+
+
+class TestLoader(unittest.TestCase):
+ @patch('json.load')
+ @patch('json.loads')
+ @patch('os.path.isfile')
+ @patch('mxnet.symbol.load')
+ def test_load_symbol_nodes(self, mock_symbol_load, mock_isfile, mock_json_loads, mock_json_load):
+ mock_isfile.return_value = True
+ mock_json_load.return_value = {'nodes': ''}
+ mock_json_loads.return_value = {'nodes': {'node1': 1}}
+ mock_symbol_load_obj = MockSymbolLoadObj()
+ mock_symbol_load.return_value = mock_symbol_load_obj
+ with patch('mo.front.mxnet.loader.open') as mock_open:
+ self.assertEqual({'node1': 1}, load_symbol_nodes("model_name", True))
+
+ @patch('json.load')
+ @patch('json.loads')
+ @patch('os.path.isfile')
+ @patch('mxnet.symbol.load')
+ def test_load_symbol_with_custom_nodes(self, mock_symbol_load, mock_isfile, mock_json_loads, mock_json_load):
+ mock_isfile.return_value = True
+ mock_json_load.return_value = {'nodes': [{'op': 'custom_op'}, {'op': 'custom_op'}]}
+ mock_json_loads.return_value = {'nodes': {'node1': 1}}
+ mock_symbol_load_obj = MockSymbolLoadObj()
+ mock_symbol_load.return_value = mock_symbol_load_obj
+ with patch('mo.front.mxnet.loader.open') as mock_open:
+ list_nodes = load_symbol_nodes("model_name", False)
+ self.assertEqual(2, len(list_nodes))
+ for node in list_nodes:
+ self.assertEqual({'op': 'custom_op'}, node)
+
+ def test_parse_input_model(self):
+ input_model = '/model-optimizer-mxnet/data/nd/vgg19-0015.params'
+ model_name, iteration_number = parse_input_model(input_model)
+ self.assertEqual(model_name, '/model-optimizer-mxnet/data/nd/vgg19')
+ self.assertEqual(iteration_number, 15)
diff --git a/model-optimizer/mo/front/onnx/extractors/constant_test.py b/model-optimizer/mo/front/onnx/extractors/constant_test.py
new file mode 100644
index 000000000..8204966ab
--- /dev/null
+++ b/model-optimizer/mo/front/onnx/extractors/constant_test.py
@@ -0,0 +1,60 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import logging as log
+import unittest
+
+import numpy as np
+import onnx
+from generator import generator, generate
+from onnx.mapping import NP_TYPE_TO_TENSOR_TYPE
+
+from mo.front.onnx.extractors.constant import onnx_constant_ext
+from mo.utils.unittest.extractors import PB
+
+dtypes = [np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.float32, np.double, np.bool]
+
+
+@generator
+class ConstantONNXExtractorTest(unittest.TestCase):
+ @staticmethod
+ def _create_constant_node(numpy_dtype):
+ numpy_dtype = np.dtype(numpy_dtype)
+ if numpy_dtype not in NP_TYPE_TO_TENSOR_TYPE:
+ log.error("Numpy type {} not supported in ONNX".format(numpy_dtype))
+ return None
+
+ values = np.array(np.random.randn(5, 5).astype(numpy_dtype))
+ pb = onnx.helper.make_node(
+ 'Constant',
+ inputs=[],
+ outputs=['values'],
+ value=onnx.helper.make_tensor(
+ name='const_tensor',
+ data_type=NP_TYPE_TO_TENSOR_TYPE[numpy_dtype],
+ dims=values.shape,
+ vals=values.flatten().astype(numpy_dtype),
+ ),
+ )
+ node = PB({'pb': pb})
+ return node
+
+ @generate(*dtypes)
+ def test_constant_ext(self, np_dtype):
+ node = self._create_constant_node(np_dtype)
+ attrs = onnx_constant_ext(node)
+ self.assertTrue(attrs['data_type'] == np_dtype,
+ 'Wrong data_type attribute: recieved {}, expected {}'.format(attrs['data_type'], np_dtype))
diff --git a/model-optimizer/mo/front/tf/extractors/concat_test.py b/model-optimizer/mo/front/tf/extractors/concat_test.py
new file mode 100644
index 000000000..054da6186
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/concat_test.py
@@ -0,0 +1,38 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from mo.front.tf.extractors.concat import tf_concat_ext
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class ConcatExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.patcher = 'mo.front.tf.extractors.concat.concat_infer'
+
+ def test_concat(self):
+ pb = PB({'attr': {
+ 'N': PB({'i': 3}),
+ }})
+ self.expected = {
+ 'type': 'Concat',
+ 'N': 3,
+ }
+ self.res = tf_concat_ext(pb=pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = (None)
+ self.compare()
diff --git a/model-optimizer/mo/front/tf/extractors/const_test.py b/model-optimizer/mo/front/tf/extractors/const_test.py
new file mode 100644
index 000000000..5caafa48f
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/const_test.py
@@ -0,0 +1,177 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+import tensorflow as tf
+from generator import generator, generate
+
+from mo.front.tf.extractors.const import tf_const_ext
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+dtypes = {"ints": [(tf.int8, np.int8),
+ (tf.int16, np.int16),
+ (tf.int32, np.int32),
+ (tf.int64, np.int64)],
+
+ "uints": [(tf.uint8, np.uint8),
+ (tf.uint16, np.uint16),
+ ],
+ "floats": [(tf.float32, np.float32),
+ (tf.float64, np.double)],
+
+ "bools": [(tf.bool, np.bool)],
+
+ "strings": [(tf.string, np.str)]}
+if tf.__version__ > "1.4.0":
+ dtypes['uints'].extend([(tf.uint32, np.uint32), (tf.uint64, np.uint64)])
+
+
+@generator
+class ConstExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.patcher = 'mo.front.tf.extractors.const.tf_const_infer'
+
+ @generate(*dtypes['ints'])
+ def test_const_ints(self, tf_dtype, np_dtype):
+ shape = [1, 1, 200, 50]
+ values = np.random.randint(low=np.iinfo(np_dtype).min, high=np.iinfo(np_dtype).max, size=shape, dtype=np_dtype)
+ tensor_proto = tf.make_tensor_proto(values=values, dtype=tf_dtype, shape=shape)
+ pb = PB({"attr": PB({
+ "value": PB({
+ "tensor": PB({
+ "dtype": tensor_proto.dtype,
+ "tensor_shape": tensor_proto.tensor_shape,
+ "tensor_content": tensor_proto.tensor_content
+ })
+ })
+ })})
+ self.expected = {
+ 'data_type': np_dtype,
+ 'shape': np.asarray(shape, dtype=np.int),
+ 'value': values
+ }
+ self.res = tf_const_ext(pb=pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*dtypes['uints'])
+ def test_const_uints(self, tf_dtype, np_dtype):
+ shape = [1, 1, 200, 50]
+ values = np.random.randint(low=np.iinfo(np_dtype).min, high=np.iinfo(np_dtype).max, size=shape, dtype=np_dtype)
+ tensor_proto = tf.make_tensor_proto(values=values, dtype=tf_dtype, shape=shape)
+ pb = PB({"attr": PB({
+ "value": PB({
+ "tensor": PB({
+ "dtype": tensor_proto.dtype,
+ "tensor_shape": tensor_proto.tensor_shape,
+ })
+ })
+ })})
+ if tf_dtype == tf.uint16:
+ setattr(pb.attr.value.tensor, "int_val", values.tolist())
+ else:
+ setattr(pb.attr.value.tensor, "tensor_content", tensor_proto.tensor_content)
+ self.expected = {
+ 'data_type': np_dtype,
+ 'shape': np.asarray(shape, dtype=np.int),
+ 'value': values
+ }
+ self.res = tf_const_ext(pb=pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*dtypes['floats'])
+ def test_const_floats(self, tf_dtype, np_dtype):
+ shape = [1, 1, 200, 50]
+ values = np.random.uniform(low=np.finfo(np.float32).min, high=np.finfo(np.float32).max, size=shape).astype(
+ np_dtype)
+ tensor_proto = tf.make_tensor_proto(values=values, dtype=tf_dtype, shape=shape)
+ pb = PB({"attr": PB({
+ "value": PB({
+ "tensor": PB({
+ "dtype": tensor_proto.dtype,
+ "tensor_shape": tensor_proto.tensor_shape,
+ "tensor_content": tensor_proto.tensor_content
+ })
+ })
+ })})
+ self.expected = {
+ 'data_type': np_dtype,
+ 'shape': np.asarray(shape, dtype=np.int),
+ 'value': values
+ }
+ self.res = tf_const_ext(pb=pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = None
+ self.compare()
+
+ # TODO: Check how to correctly handle tensor_proto with booleans. It has no tensor_content section
+ @generate(*dtypes['bools'])
+ def test_const_floats(self, tf_dtype, np_dtype):
+ shape = [1, 1, 50, 50]
+ values = np.random.choice(a=[True, False], size=shape, p=[0.5, 0.5])
+ tensor_proto = tf.make_tensor_proto(values=values, dtype=tf_dtype, shape=shape)
+ pb = PB({"attr": PB({
+ "value": PB({
+ "tensor": PB({
+ "dtype": tensor_proto.dtype,
+ "tensor_shape": tensor_proto.tensor_shape,
+ "bool_val": values.tolist()
+ })
+ })
+ })})
+ self.expected = {
+ 'data_type': np_dtype,
+ 'shape': np.asarray(shape, dtype=np.int),
+ 'value': values
+ }
+ self.res = tf_const_ext(pb=pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = None
+ self.compare()
+ # TODO: Check how to correctly create tensor_proto with strings
+ # @generate(*dtypes['strings'])
+ # def test_const_floats(self, tf_dtype, np_dtype):
+ # shape = [1, 1, 50, 50]
+ # values = np.chararray(shape=shape)
+ # values[:] = "bla"
+ # tensor_proto = tf.make_tensor_proto(values=values, dtype=tf_dtype, shape=shape)
+ # pb = PB({"attr": PB({
+ # "value": PB({
+ # "tensor": PB({
+ # "dtype": tensor_proto.dtype,
+ # "tensor_shape": tensor_proto.tensor_shape,
+ # "tensor_content": tensor_proto.tensor_content
+ # })
+ # })
+ # })})
+ # self.expected = {
+ # 'data_type': np_dtype,
+ # 'shape': np.asarray(shape, dtype=np.int),
+ # 'value': values
+ # }
+ # self.res = tf_const_ext(pb=pb)
+ # self.res["infer"](None)
+ # self.call_args = self.infer_mock.call_args
+ # self.expected_call_args = None
+ # self.compare()
diff --git a/model-optimizer/mo/front/tf/extractors/eltwise_test.py b/model-optimizer/mo/front/tf/extractors/eltwise_test.py
new file mode 100644
index 000000000..0a0f1e30d
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/eltwise_test.py
@@ -0,0 +1,357 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import numpy as np
+import tensorflow as tf
+from generator import generator, generate
+
+from mo.front.tf.extractor import tf_op_extractors
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+dtypes_map = [(1, np.float32), (2, np.float64), (3, np.int32), (4, np.uint8), (5, np.int16), (6, np.int8),
+ (7, str), (9, np.int64), (10, bool), (17, np.uint16)]
+
+if tf.__version__ > "1.4.0":
+ dtypes_map.extend([(22, np.uint32), (23, np.uint64)])
+
+
+@generator
+class EltwiseExtractorTest(BaseExtractorsTestingClass):
+ def check_lambda_res(self, actual, expected, expected_type):
+ self.assertEqual(expected_type, type(actual), "Eltwise lambda function results has wrong data type!")
+ if isinstance(actual, np.ndarray) and isinstance(expected, np.ndarray):
+ np.testing.assert_equal(actual, expected)
+ else:
+ self.assertEqual(expected, actual, "Eltwise lambda function results validation failed!")
+
+ @classmethod
+ def setUpClass(cls):
+ cls.patcher = 'mo.front.tf.extractors.eltwise.eltwise_infer'
+
+ @generate(*dtypes_map)
+ def test_eltwise_dtypes_map(self, dtype, np_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": dtype
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'can_be_bias': True,
+ 'data_type': np_type,
+ 'operation': 'sum',
+ 'type': 'Eltwise'
+ }
+ self.res = tf_op_extractors['Add'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[((1, 2), 3, int), ((1., 2.), 3., float), (('a', 'b'), 'ab', str),
+ ((np.full(shape=(10, 10), fill_value=4), np.full(shape=(10, 10), fill_value=2)),
+ np.full(shape=(10, 10), fill_value=6), np.ndarray)
+ ])
+ def test_eltwise_add(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'can_be_bias': True,
+ 'data_type': np.float32,
+ 'operation': 'sum',
+ 'type': 'Eltwise'
+ }
+ self.res = tf_op_extractors['Add'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(*lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[((1, 2), 2, int), ((1., 2.), 2., float),
+ ((np.full(shape=(10, 10), fill_value=4), np.full(shape=(10, 10), fill_value=2)),
+ np.full(shape=(10, 10), fill_value=8), np.ndarray)])
+ def test_eltwise_mul(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'data_type': np.float32,
+ 'operation': 'mul',
+ 'type': 'Eltwise'
+ }
+ self.res = tf_op_extractors['Mul'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(*lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[(4, 0.5, np.float64),
+ (np.full(shape=(10, 10), fill_value=4), np.full(shape=(10, 10), fill_value=0.5), np.ndarray)])
+ def test_eltwise_rsqrt(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'data_type': np.float32,
+ 'type': 'Power',
+ 'power': -0.5,
+ 'scale': 1,
+ 'shift': 0
+ }
+ self.res = tf_op_extractors['Rsqrt'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[(1, -1, int), (1., -1., float),
+ (np.full(shape=(10, 10), fill_value=4), np.full(shape=(10, 10), fill_value=-4), np.ndarray)])
+ def test_eltwise_neg(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'data_type': np.float32,
+ 'type': 'Power',
+ 'power': 1,
+ 'scale': -1,
+ 'shift': 0
+ }
+ self.res = tf_op_extractors['Neg'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[((1, 2), -1, int), ((1., 2.), -1., float),
+ ((np.full(shape=(10, 10), fill_value=4), np.full(shape=(10, 10), fill_value=2)),
+ np.full(shape=(10, 10), fill_value=2), np.ndarray)
+ ])
+ def test_eltwise_sub(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'data_type': np.float32,
+ }
+ self.res = tf_op_extractors['Sub'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(*lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[(4, 4, np.int64), (4., 4., np.float64),
+ (-1, 0, np.int64), (-1., 0, np.float64),
+ (np.full(shape=(3, 3), fill_value=-1), np.zeros(shape=(3, 3)), np.ndarray),
+ (np.full(shape=(3, 3), fill_value=4), np.full(shape=(3, 3), fill_value=4), np.ndarray)])
+ def test_eltwise_relu(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'data_type': np.float32,
+ "type": "ReLU"
+ }
+ self.res = tf_op_extractors['Relu'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[(4, 4, np.int64), (4., 4., np.float64),
+ (-1, 0, np.int64), (-1., 0, np.float64),
+ (10, 6, np.int64), (10., 6, np.float64),
+ (np.full(shape=(3, 3), fill_value=-1), np.zeros(shape=(3, 3)), np.ndarray),
+ (np.full(shape=(3, 3), fill_value=10), np.full(shape=(3, 3), fill_value=6), np.ndarray),
+ (np.full(shape=(3, 3), fill_value=4), np.full(shape=(3, 3), fill_value=4), np.ndarray)])
+ def test_eltwise_relu6(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'data_type': np.float32,
+ 'type': 'Clamp',
+ 'min': 0,
+ 'max': 6
+ }
+ self.res = tf_op_extractors['Relu6'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[(4, 16, int), (4., 16., float),
+ (-1, 1, int), (-1., 1., float),
+ (np.full(shape=(3, 3), fill_value=-1), np.full(shape=(3, 3), fill_value=1), np.ndarray),
+ (np.full(shape=(3, 3), fill_value=4), np.full(shape=(3, 3), fill_value=16), np.ndarray)])
+ def test_eltwise_square(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'data_type': np.float32,
+ }
+ self.res = tf_op_extractors['Square'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[
+ ((4, 16), 16, np.int64),
+ ((4., 16.), 16., np.float64),
+ ((-1, 1), 1, np.int64),
+ ((-1., 1), 1., np.float64),
+ (
+ (
+ np.full(shape=(3, 3), fill_value=-1),
+ np.full(shape=(3, 3), fill_value=-2)
+ ),
+ np.full(shape=(3, 3), fill_value=-1),
+ np.ndarray
+ ),
+ (
+ (
+ np.full(shape=(3, 3), fill_value=4),
+ np.full(shape=(3, 3), fill_value=0)
+ ),
+ np.full(shape=(3, 3), fill_value=4),
+ np.ndarray)
+ ])
+ def test_eltwise_maximum(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'data_type': np.float32,
+ }
+ self.res = tf_op_extractors['Maximum'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(*lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
+
+ @generate(*[((16, 4), 4, float), ((4., 16.), 0.25, float),
+ ((-1, 1), -1, float), ((-16., 1), -16., float),
+ ((np.full(shape=(3, 3), fill_value=-1), np.full(shape=(3, 3), fill_value=-2)),
+ np.full(shape=(3, 3), fill_value=0.5), np.ndarray),
+ ((np.full(shape=(3, 3), fill_value=4), np.full(shape=(3, 3), fill_value=0)),
+ np.full(shape=(3, 3), fill_value=np.inf), np.ndarray)])
+ def test_eltwise_realdiv(self, lambda_args, expected_res, expected_type):
+ node_pb = PB({
+ 'pb': PB({
+ 'attr': PB({
+ 'T': PB({
+ "type": 1
+ })
+ })
+ })
+ })
+ self.expected = {
+ 'data_type': np.float32,
+ }
+ self.res = tf_op_extractors['RealDiv'](node_pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ eltwise_lambda = self.call_args[0][1]
+ lambda_res = eltwise_lambda(*lambda_args)
+ self.check_lambda_res(actual=lambda_res, expected=expected_res, expected_type=expected_type)
+ self.expected_call_args = None
+ self.compare()
diff --git a/model-optimizer/mo/front/tf/extractors/expand_dims_test.py b/model-optimizer/mo/front/tf/extractors/expand_dims_test.py
new file mode 100644
index 000000000..dd1f1d8c7
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/expand_dims_test.py
@@ -0,0 +1,32 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from mo.front.tf.extractors.expand_dims import tf_expand_dims_ext
+from mo.utils.unittest.extractors import BaseExtractorsTestingClass
+
+
+class ExpandDimsExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.patcher = 'mo.front.tf.extractors.expand_dims.tf_expand_dims_infer'
+
+ def test_expand_dims(self):
+ self.expected = {}
+ self.res = tf_expand_dims_ext(pb=None)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = None
+ self.compare()
diff --git a/model-optimizer/mo/front/tf/extractors/identity_test.py b/model-optimizer/mo/front/tf/extractors/identity_test.py
new file mode 100644
index 000000000..1a6a84f9b
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/identity_test.py
@@ -0,0 +1,32 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from mo.front.tf.extractors.identity import tf_identity_ext
+from mo.utils.unittest.extractors import BaseExtractorsTestingClass
+
+
+class IdentityExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.patcher = 'mo.front.tf.extractors.identity.copy_shape_infer'
+
+ def test_identity(self):
+ self.expected = {}
+ self.res = tf_identity_ext(pb=None)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = None
+ self.compare()
diff --git a/model-optimizer/mo/front/tf/extractors/lrn_test.py b/model-optimizer/mo/front/tf/extractors/lrn_test.py
new file mode 100644
index 000000000..b4855b671
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/lrn_test.py
@@ -0,0 +1,53 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.front.tf.extractors.lrn import tf_lrn_ext
+from mo.utils.unittest.extractors import PB
+
+
+class LRNExtractorTest(unittest.TestCase):
+ """
+ Unit Test:
+ 1. test_bias_check - check if bias is not 1
+ 2. test_simple_check - check IE parameters calculations
+
+ """
+
+ def test_simple_check(self):
+ # Input parameters for LRN extactor
+ # taken from ICV AlexNet LRN layer
+ pb = PB({'attr': {
+ 'alpha': PB({'f': 0.000019999999494757503}),
+ 'beta': PB({'f': 0.75}),
+ 'bias': PB({'f':2.0}),
+ 'depth_radius': PB({'i': 2}),
+ }})
+ res = tf_lrn_ext(pb)
+ # Reference results for given parameters
+ ref = {
+ 'type': 'Norm',
+ 'alpha': 9.999999747378752e-05,
+ 'beta': 0.75,
+ 'bias': 2.0,
+ 'local_size': 5,
+ 'region': 'across',
+ 'infer': copy_shape_infer,
+ }
+ for attr in ref:
+ self.assertEqual(res[attr], ref[attr])
diff --git a/model-optimizer/mo/front/tf/extractors/matmul_test.py b/model-optimizer/mo/front/tf/extractors/matmul_test.py
new file mode 100644
index 000000000..e7bd52410
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/matmul_test.py
@@ -0,0 +1,43 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from mo.front.tf.extractors.matmul import tf_matmul_ext
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class MatmulExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.patcher = 'mo.front.tf.extractors.matmul.tf_matmul_infer'
+
+ def test_matmul(self):
+ pb = PB({'attr': {
+ 'transpose_a': PB({
+ 'b': True
+ }),
+ 'transpose_b': PB({
+ 'b': False
+ }),
+ }})
+ self.expected = {
+ 'transpose_a': True,
+ 'transpose_b': False,
+ }
+ self.res = tf_matmul_ext(pb=pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = None
+ self.compare()
diff --git a/model-optimizer/mo/front/tf/extractors/mean_test.py b/model-optimizer/mo/front/tf/extractors/mean_test.py
new file mode 100644
index 000000000..7430bae42
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/mean_test.py
@@ -0,0 +1,42 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from mo.front.tf.extractors.mean import tf_mean_ext
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class MatmulExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.patcher = 'mo.front.tf.extractors.mean.tf_reduce_infer'
+
+ def test_matmul(self):
+ pb = PB({'attr': {
+ 'keep_dims': PB({
+ 'b': True
+ }),
+ }})
+ self.expected = {
+ 'type': "Pooling",
+ 'keep_dims': True,
+ }
+ self.res = tf_mean_ext(pb=pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = (None, np.add.reduce)
+ self.compare()
diff --git a/model-optimizer/mo/front/tf/extractors/prod_test.py b/model-optimizer/mo/front/tf/extractors/prod_test.py
new file mode 100644
index 000000000..53b974d86
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/prod_test.py
@@ -0,0 +1,41 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from mo.front.tf.extractors.prod import tf_reduce_prod_ext
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class ProdExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.patcher = 'mo.front.tf.extractors.prod.tf_reduce_infer'
+
+ def test_prod(self):
+ pb = PB({'attr': {
+ 'keep_dims': PB({
+ 'b': True
+ }),
+ }})
+ self.expected = {
+ 'keep_dims': True,
+ }
+ self.res = tf_reduce_prod_ext(pb=pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = (None, np.multiply.reduce)
+ self.compare()
diff --git a/model-optimizer/mo/front/tf/extractors/squeeze_test.py b/model-optimizer/mo/front/tf/extractors/squeeze_test.py
new file mode 100644
index 000000000..ccc0ff18b
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/squeeze_test.py
@@ -0,0 +1,42 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import numpy as np
+
+from mo.front.tf.extractors.squeeze import tf_squeeze_ext
+from mo.utils.unittest.extractors import PB, BaseExtractorsTestingClass
+
+
+class SqueezeExtractorTest(BaseExtractorsTestingClass):
+ @classmethod
+ def setUpClass(cls):
+ cls.patcher = 'mo.front.tf.extractors.squeeze.tf_squeeze_infer'
+
+ def test_squeeze(self):
+ pb = PB({'attr': {
+ 'squeeze_dims': PB({
+ 'list': PB({'i': [1, 2]})
+ })
+ }})
+ self.expected = {
+ 'type': 'Reshape',
+ 'squeeze_dims': np.array([1, 2], dtype=np.int8),
+ }
+ self.res = tf_squeeze_ext(pb=pb)
+ self.res["infer"](None)
+ self.call_args = self.infer_mock.call_args
+ self.expected_call_args = None
+ self.compare()
diff --git a/model-optimizer/mo/front/tf/extractors/utils_test.py b/model-optimizer/mo/front/tf/extractors/utils_test.py
new file mode 100644
index 000000000..51544cd08
--- /dev/null
+++ b/model-optimizer/mo/front/tf/extractors/utils_test.py
@@ -0,0 +1,44 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.front.tf.extractors.utils import collect_tf_attrs
+from mo.utils.unittest.extractors import PB
+
+
+class AttrParsingTest(unittest.TestCase):
+ def test_simple_check(self):
+ pb = PB({'attr': {
+ 'str': PB({'s': "aaaa"}),
+ 'int': PB({'i': 7}),
+ 'float': PB({'f': 2.0}),
+ 'bool': PB({'b': True}),
+ 'lisint': PB({'list': PB({'i': 5, 'i': 6})})}
+ })
+
+ res = collect_tf_attrs(pb.attr)
+
+ # Reference results for given parameters
+ ref = {
+ 'str': pb.attr['str'].s,
+ 'int': pb.attr['int'].i,
+ 'float': pb.attr['float'].f,
+ 'bool': pb.attr['bool'].b,
+ 'lisint': pb.attr['lisint'].list.i
+ }
+ for attr in ref:
+ self.assertEqual(res[attr], ref[attr])
diff --git a/model-optimizer/mo/front/tf/loader_test.py b/model-optimizer/mo/front/tf/loader_test.py
new file mode 100644
index 000000000..326849ff2
--- /dev/null
+++ b/model-optimizer/mo/front/tf/loader_test.py
@@ -0,0 +1,34 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest.mock
+from io import StringIO
+from unittest.mock import Mock, MagicMock
+
+from generator import generate, generator
+
+from mo.front.tf.loader import load_tf_graph_def
+
+
+@generator
+class TestLoader(unittest.TestCase):
+ @generate('/path/to/somewhere/my_checkpoint.ckpt', '/path/to/somewhere/my_meta_graph.meta')
+ @unittest.mock.patch('sys.stdout', new_callable=StringIO)
+ def test_helper_print_ckpt(self, path, out):
+ mock = Mock(__bool__=MagicMock(side_effect=Exception()))
+ self.assertRaises(Exception, load_tf_graph_def, path, meta_graph_file=mock)
+ self.assertRegex(out.getvalue(),
+ '\[ WARNING ] The value for the --input_model command line parameter ends with "\.ckpt"')
diff --git a/model-optimizer/mo/graph/graph_test.py b/model-optimizer/mo/graph/graph_test.py
new file mode 100644
index 000000000..6b5d9905e
--- /dev/null
+++ b/model-optimizer/mo/graph/graph_test.py
@@ -0,0 +1,324 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.graph.graph import erase_node, get_node_id_by_name, Node, replace_node, get_inputs_with_ports
+from mo.ops.const import Const
+from mo.utils.error import Error
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes = {
+ '0': {'name': 'input1', 'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ '1': {'name': 'input2', 'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ '2': {'name': 'node_1', 'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'NotPlaceholder'},
+ '3': {'name': 'node_2', 'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'NotPlaceholder'},
+ '4': {'name': 'node_3', 'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'NotPlaceholder'},
+ '5': {'name': 'node_4', 'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'NotPlaceholder'},
+ '6': {'name': 'output', 'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'OpOutput',
+ 'is_output': True},
+ 'input_3': {'name': 'input_3', 'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'}
+}
+edges = {
+ ('0', '2'),
+ ('2', '3'),
+ ('4', '6'),
+ ('1', '5'),
+ ('5', '6'),
+ ('input_3', '6')
+}
+
+
+class TestGetNodeById(unittest.TestCase):
+ def setUp(self):
+ self.graph = build_graph(nodes, edges)
+
+ def test_get_node_id_by_name(self):
+ self.assertEqual(get_node_id_by_name(self.graph, 'input1'), '0')
+
+ def test_get_node_id_by_name_1(self):
+ self.assertEqual(get_node_id_by_name(self.graph, 'input2'), '1')
+
+ def test_get_node_id_by_name_2(self):
+ self.assertEqual(get_node_id_by_name(self.graph, 'node_1'), '2')
+
+ def test_get_node_id_by_name_3(self):
+ self.assertEqual(get_node_id_by_name(self.graph, 'node_2'), '3')
+
+ def test_get_node_id_by_name_4(self):
+ self.assertEqual(get_node_id_by_name(self.graph, 'node_3'), '4')
+
+ def test_get_node_id_by_name_5(self):
+ self.assertEqual(get_node_id_by_name(self.graph, 'node_4'), '5')
+
+ def test_get_node_id_by_name_6(self):
+ self.assertEqual(get_node_id_by_name(self.graph, 'output'), '6')
+
+ def test_get_node_id_by_name_7(self):
+ self.assertEqual(get_node_id_by_name(self.graph, 'input_3'), 'input_3')
+
+ def test_get_node_id_by_name_8(self):
+ self.assertRaises(Error, get_node_id_by_name, self.graph, '1')
+
+
+class TestEraseNode(unittest.TestCase):
+ def test_remove_noop_nodes_middle(self):
+ graph = build_graph(
+ {
+ 'input': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'noop': {'type': 'NoOp', 'value': None, 'kind': 'op'},
+ 'output': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('input', 'noop'),
+ ('noop', 'output')])
+
+ self.assertEqual(len(graph.nodes()), 3)
+ self.assertEqual(len(graph.edges()), 2)
+ self.assertListEqual(list(graph.out_edges('input')), [('input', 'noop')])
+
+ erase_node(Node(graph, 'noop'))
+
+ self.assertEqual(len(graph.nodes()), 2)
+ self.assertEqual(len(graph.edges()), 1)
+ self.assertListEqual(list(graph.out_edges('input')), [('input', 'output')])
+
+ def test_remove_noop_nodes_middle_2(self):
+ graph = build_graph(
+ {
+ 'input': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'noop': {'type': 'NoOp', 'value': None, 'kind': 'op'},
+ 'output_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('input', 'noop'),
+ ('noop', 'output_1', {'in': 4, 'out': 0}),
+ ('noop', 'output_2', {'in': 2, 'out': 0}),
+ ('noop', 'output_3', {'in': 10, 'out': 0})])
+
+ ref_graph = build_graph(
+ {
+ 'input': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'output_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('input', 'output_1', {'in': 4, 'out': 0}),
+ ('input', 'output_2', {'in': 2, 'out': 0}),
+ ('input', 'output_3', {'in': 10, 'out': 0})],
+ nodes_with_edges_only=True)
+
+ erase_node(Node(graph, 'noop'))
+
+ compare_graphs(graph, ref_graph, 'output_1')
+
+ def test_remove_noop_nodes_check_out_port(self):
+ graph = build_graph(
+ {
+ 'input': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'noop': {'type': 'NoOp', 'value': None, 'kind': 'op'},
+ 'output_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('input', 'noop'),
+ ('noop', 'output_1', {'in': 4, 'out': 1}),
+ ('noop', 'output_2', {'in': 2, 'out': 1}),
+ ('noop', 'output_3', {'in': 10, 'out': 1})])
+
+ ref_graph = build_graph(
+ {
+ 'input': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'output_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('input', 'output_1', {'in': 4, 'out': 0}),
+ ('input', 'output_2', {'in': 2, 'out': 0}),
+ ('input', 'output_3', {'in': 10, 'out': 0})],
+ nodes_with_edges_only=True)
+
+ erase_node(Node(graph, 'noop'))
+
+ compare_graphs(graph, ref_graph, 'output_1')
+
+ def test_remove_noop_nodes_too_many_outputs(self):
+ graph = build_graph(
+ {
+ 'input': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'noop': {'type': 'NoOp', 'value': None, 'kind': 'op'},
+ 'output_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('input', 'noop'),
+ ('noop', 'output_1', {'in': 4, 'out': 0}),
+ ('noop', 'output_2', {'in': 2, 'out': 1}),
+ ('noop', 'output_3', {'in': 10, 'out': 0})])
+
+ self.assertRaises(AssertionError, erase_node, Node(graph, 'noop'))
+
+ def test_remove_noop_nodes_front(self):
+ graph = build_graph(
+ {
+ 'noop': {'type': 'NoOp', 'value': None, 'kind': 'op'},
+ 'output': {'type': 'Identity', 'value': None, 'kind': 'op'}
+ },
+ [('noop', 'output')]
+ )
+
+ self.assertEqual(len(graph.nodes()), 2)
+ self.assertEqual(len(graph.edges()), 1)
+ self.assertListEqual(list(graph.out_edges('noop')), [('noop', 'output')])
+
+ erase_node(Node(graph, 'noop'))
+
+ self.assertEqual(len(graph.nodes()), 1)
+ self.assertEqual(len(graph.edges()), 0)
+ self.assertEqual(len(graph.in_edges('output')), 0)
+
+ def test_remove_noop_nodes_back(self):
+ graph = build_graph(
+ {
+ 'input': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'noop': {'type': 'NoOp', 'value': None, 'kind': 'op'}
+ },
+ [('input', 'noop')]
+ )
+
+ self.assertEqual(len(graph.nodes()), 2)
+ self.assertEqual(len(graph.edges()), 1)
+ self.assertListEqual(list(graph.in_edges('noop')), [('input', 'noop')])
+
+ erase_node(Node(graph, 'noop'))
+
+ self.assertEqual(len(graph.nodes()), 1)
+ self.assertEqual(len(graph.edges()), 0)
+ self.assertEqual(len(graph.in_edges('input')), 0)
+
+ def test_remove_noop_nodes_noop_only(self):
+ import networkx as nx
+ graph = nx.MultiDiGraph()
+ graph.add_node('noop', **{'type': 'NoOp', 'value': None, 'kind': 'op'})
+
+ self.assertEqual(len(graph.nodes()), 1)
+ self.assertEqual(len(graph.edges()), 0)
+
+ erase_node(Node(graph, 'noop'))
+
+ self.assertEqual(len(graph.nodes()), 0)
+ self.assertEqual(len(graph.edges()), 0)
+
+ def test_remove_noop_error(self):
+ graph = build_graph(
+ {
+ 'input_1': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'input_2': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'input_3': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'noop': {'type': 'NoOp', 'value': None, 'kind': 'op'},
+ 'output_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('input_1', 'noop'),
+ ('input_2', 'noop'),
+ ('input_3', 'noop'),
+ ('noop', 'output_1'),
+ ('noop', 'output_2'),
+ ('noop', 'output_3')])
+ self.assertRaises(AssertionError, erase_node, Node(graph, 'noop'))
+
+
+class TestReplaceNode(unittest.TestCase):
+ def test_replace_node_one_consumer(self):
+ graph = build_graph(
+ {
+ 'input_1': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'input_2': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'old': {'type': 'Identity', 'value': None, 'kind': 'op', 'is_output': True},
+ 'output': {'type': 'OpOutput', 'value': None, 'kind': 'op'},
+ },
+ [('input_1', 'old'),
+ ('input_2', 'old'),
+ ('old', 'output')])
+
+ new_node = Const(graph, {'name': 'new'}).create_node([Node(graph, 'input_1'), Node(graph, 'input_2')])
+ replace_node(Node(graph, 'old'), new_node)
+
+ self.assertEqual(len(graph.nodes()), 4)
+ self.assertEqual(len(graph.edges()), 3)
+ self.assertEqual(new_node['is_output'], True)
+ self.assertListEqual(list(graph.out_edges('new')), [('new', 'output')])
+
+ def test_replace_node_several_consumers(self):
+ graph = build_graph(
+ {
+ 'input_1': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'input_2': {'type': 'Placeholder', 'value': None, 'kind': 'op'},
+ 'old': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'output_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ },
+ [('input_1', 'old'),
+ ('input_2', 'old'),
+ ('old', 'output_3'),
+ ('old', 'output_2'),
+ ('old', 'output_1'),
+ ])
+
+ new_node = Const(graph, {'name': 'new'}).create_node([Node(graph, 'input_1'), Node(graph, 'input_2')])
+ replace_node(Node(graph, 'old'), new_node)
+
+ self.assertEqual(len(graph.nodes()), 6)
+ self.assertEqual(len(graph.edges()), 5)
+ self.assertListEqual(sorted(graph.out_edges('new')), [('new', 'output_1'), ('new', 'output_2'),
+ ('new', 'output_3')])
+ expected_result = [('new', 'output_1', {'in': 0, 'out': 2, 'name': 'old'}),
+ ('new', 'output_2', {'in': 0, 'out': 1, 'name': 'old'}),
+ ('new', 'output_3', {'in': 0, 'out': 0, 'name': 'old'})]
+ self.assertListEqual(sorted(graph.out_edges('new', data=True)), expected_result)
+
+
+class GetNodesWithPorts(unittest.TestCase):
+ def test_get_nodes_with_ports(self):
+ nodes = {
+ 'one': {},
+ 'two': {},
+ 'three': {},
+ 'four': {},
+ 'five': {}
+ }
+ edges = [
+ ('one', 'two', {'in': 0, 'out': 0}),
+ ('two', 'three', {'in': 0, 'out': 0}),
+ ('two', 'four', {'in': 0, 'out': 1}),
+ ('two', 'five', {'in': 0, 'out': 2}),
+ ('three', 'five', {'in': 1, 'out': 0})
+ ]
+ graph = build_graph(nodes, edges)
+ match = {
+ 'one': Node(graph, 'one'),
+ 'two': Node(graph, 'two'),
+ 'three': Node(graph, 'three'),
+ 'four': Node(graph, 'four'),
+ 'five': Node(graph, 'five'),
+
+ }
+ input_names_in_pattern = ['one', 'three']
+ result = get_inputs_with_ports(graph=graph, match=match, pattern_edges=edges,
+ input_names_in_pattern=input_names_in_pattern)
+ self.assertListEqual([(match['one'], 0), (match['three'], 0)], result)
diff --git a/model-optimizer/mo/main_test.py b/model-optimizer/mo/main_test.py
new file mode 100644
index 000000000..79f9feb60
--- /dev/null
+++ b/model-optimizer/mo/main_test.py
@@ -0,0 +1,31 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import argparse
+import unittest
+from unittest.mock import patch
+
+from mo.main import main
+from mo.utils.error import FrameworkError
+
+
+class TestMainErrors(unittest.TestCase):
+ @patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
+ @patch('mo.main.driver', side_effect=FrameworkError('FW ERROR MESSAGE'))
+ def test_FrameworkError(self, mock_argparse, mock_driver):
+ with self.assertLogs() as logger:
+ main(argparse.ArgumentParser(), 'framework_string')
+ self.assertEqual(logger.output, ['ERROR:root:FW ERROR MESSAGE'])
diff --git a/model-optimizer/mo/middle/passes/conv_test.py b/model-optimizer/mo/middle/passes/conv_test.py
new file mode 100644
index 000000000..ad4e3aa0f
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/conv_test.py
@@ -0,0 +1,163 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.middle.passes.conv import convert_muladd_to_scaleshift_or_power
+from mo.middle.passes.eliminate import graph_clean_up
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ScaleShift layer
+ 'scaleshift_1': {'type': 'ScaleShift', 'value': None, 'kind': 'op', 'op': 'ScaleShift'},
+ 'scaleshift_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Mul and Add operations
+ 'mul_1': {'value': None, 'kind': 'op', 'op': 'Mul'},
+ 'mul_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mul_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'add_1': {'value': None, 'kind': 'op', 'op': 'Add'},
+ 'add_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'add_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Power layer
+ 'power_1': {'type': 'Power', 'kind': 'op', 'op': 'Power', 'scale': None, 'shift': None, 'power': None},
+ 'power_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class MulAddToScaleShiftOrPower(unittest.TestCase):
+ def _create_graph_with_mul_add(self, mul_w, add_w):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3]), 'is_output': True},
+ 'mul_1_w': {'shape': np.array(mul_w.shape) if mul_w is not None else None,
+ 'value': np.array(mul_w) if mul_w is not None else None},
+ 'add_1_w': {'shape': np.array(add_w.shape) if add_w is not None else None,
+ 'value': np.array(add_w) if add_w is not None else None},
+ })
+ del graph['mul_1']['mul_1_data'][0]['in']
+ del graph['add_1']['add_1_data'][0]['in']
+ return graph
+
+ def test_mul_add_to_scaleshift_1(self):
+ graph = self._create_graph_with_mul_add(np.array([1, 2, 3]), np.array([1, 2, 3]))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ],
+ {'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ convert_muladd_to_scaleshift_or_power(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+ def test_mul_add_to_power_1(self):
+ graph = self._create_graph_with_mul_add(np.array([3]), np.array([2]))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'power_1'),
+ ('power_1', 'power_1_data'),
+ ],
+ {'power_1': {'scale': 3, 'shift': 2, 'power': 1},
+ 'power_1_data': {'is_output': True}
+ })
+
+ convert_muladd_to_scaleshift_or_power(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'power_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_mul_add_neg_1(self):
+ graph = self._create_graph_with_mul_add(None, np.array([2]))
+ graph_ref = self._create_graph_with_mul_add(None, np.array([2]))
+
+ convert_muladd_to_scaleshift_or_power(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'add_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_mul_add_neg_2(self):
+ graph = self._create_graph_with_mul_add(np.array([2]), None)
+ graph_ref = self._create_graph_with_mul_add(np.array([2]), None)
+
+ convert_muladd_to_scaleshift_or_power(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'add_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_mul_add_neg_3(self):
+ graph = self._create_graph_with_mul_add(None, None)
+ graph_ref = self._create_graph_with_mul_add(None, None)
+
+ convert_muladd_to_scaleshift_or_power(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'add_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_mul_add_neg_4(self):
+ graph = self._create_graph_with_mul_add(np.array([1, 2, 3]), np.array([3]))
+ graph_ref = self._create_graph_with_mul_add(np.array([1, 2, 3]), np.array(3))
+
+ convert_muladd_to_scaleshift_or_power(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'add_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_mul_add_neg_5(self):
+ graph = self._create_graph_with_mul_add(np.array([3]), np.array([3, 2, 1]))
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1'),
+ ('scaleshift_1', 'add_1_data'),
+ ],
+ {'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array([3, 3, 3])},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([3, 2, 1])},
+ 'add_1_data': {'is_output': True}
+ })
+
+ convert_muladd_to_scaleshift_or_power(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'add_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/model-optimizer/mo/middle/passes/eliminate_test.py b/model-optimizer/mo/middle/passes/eliminate_test.py
new file mode 100644
index 000000000..79b892c88
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/eliminate_test.py
@@ -0,0 +1,171 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node, erase_node
+from mo.middle.passes.eliminate import mark_output_reachable_nodes, graph_clean_up, \
+ get_nodes_with_attributes, mark_const_producer_nodes
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'placeholder_1': {'type': 'Placeholder', 'kind': 'op'},
+ 'placeholder_2': {'type': 'Placeholder', 'kind': 'op'},
+ 'node_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_4': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_5': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_6': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'placeholder_1_data_node': {'value': None, 'kind': 'data'},
+ 'placeholder_2_data_node': {'value': None, 'kind': 'data'},
+ 'data_node_1': {'value': None, 'kind': 'data'},
+ 'data_node_2': {'value': None, 'kind': 'data'},
+ 'data_node_3': {'value': None, 'kind': 'data'},
+ 'data_node_3_2': {'value': None, 'kind': 'data'},
+ 'data_node_4': {'value': None, 'kind': 'data'},
+ 'data_node_5': {'value': None, 'kind': 'data'},
+ 'data_node_6': {'value': None, 'kind': 'data'},
+ 'tf_call_1': {'type': 'TFCustomSubgraphCall', 'kind': 'op'},
+ 'tf_call_2': {'type': 'TFCustomSubgraphCall', 'kind': 'op'},
+ 'tf_call_3': {'type': 'TFCustomSubgraphCall', 'kind': 'op'},
+ }
+
+
+class TestEliminatePass(unittest.TestCase):
+ def test_mark_output_unreachable_nodes(self):
+ """
+ Checks that all nodes that are unreachable from output nodes are marked correspondingly.
+ The graph doesn't contain data nodes yet.
+ "node_4" is output.
+
+ placeholder_1->node_1->node_2
+ \
+ -> node_3->node_4
+
+ :return: None
+ """
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'node_1'),
+ ('node_1', 'node_2'),
+ ('placeholder_1', 'node_3'),
+ ('node_3', 'node_4')],
+ {'node_4': {'is_output': True}},
+ nodes_with_edges_only=True)
+ mark_output_reachable_nodes(graph)
+
+ self.assertListEqual(sorted(['placeholder_1', 'node_3', 'node_4']),
+ sorted(get_nodes_with_attributes(graph, is_output_reachable=True)))
+ self.assertListEqual(sorted(['node_1', 'node_2']),
+ sorted(get_nodes_with_attributes(graph, is_output_reachable=False)))
+
+ def test_mark_output_unreachable_nodes_behind_output(self):
+ """
+ Checks case when unreachable node is 'behind' (i.e. is the child) of the output node.
+ The graph doesn't contain data nodes yet.
+ "node_2" is output.
+
+ placeholder_1->node_1->node_2->node_3
+
+ :return: None
+ """
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'node_1'),
+ ('node_1', 'node_2'),
+ ('node_2', 'node_3')],
+ {'node_2': {'is_output': True}},
+ nodes_with_edges_only=True)
+ mark_output_reachable_nodes(graph)
+
+ self.assertListEqual(sorted(['placeholder_1', 'node_1', 'node_2']),
+ sorted(get_nodes_with_attributes(graph, is_output_reachable=True)))
+ self.assertFalse(graph.node['node_3']['is_output_reachable'])
+
+ def test_mark_ops_producing_constant_values(self):
+ """
+ Checks case when operation produces only constant tensors so it could be removed. If the node produces several
+ tensors and at least one of them is not constant then we should not mark this node.
+ The graph contains data nodes.
+ "data_node_2" and "data_node_5" are output.
+ "node_3" produces constant tensor "data_node_3" and non-constant tensor "data_node_3_2".
+ "node_6" produces constant tensor "data_node_6".
+ "node_4" could be eliminated since it gets constant input.
+
+ node_6->data_node_6->
+ \
+ placeholder_1->placeholder_1_data_node->node_1->data_node_1->node_2->data_node_2
+ /
+ node_3->data_node_3->node_4->data_node_4->
+ \
+ ->data_node_3_2->node_5->data_node_5
+
+ :return: None
+ """
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data_node'),
+ ('placeholder_1_data_node', 'node_1'),
+ ('node_1', 'data_node_1'),
+ ('data_node_1', 'node_2'),
+ ('node_2', 'data_node_2'),
+ ('node_3', 'data_node_3'),
+ ('node_3', 'data_node_3_2'),
+ ('node_6', 'data_node_6'),
+ ('data_node_6', 'node_1'),
+ ('data_node_3_2', 'node_5'),
+ ('node_5', 'data_node_5'),
+ ('data_node_3', 'node_4'),
+ ('data_node_4', 'node_1')],
+ {'data_node_2': {'is_output': True},
+ 'data_node_5': {'is_output': True},
+ 'data_node_3': {'value': np.array(1)},
+ 'data_node_6': {'value': np.array(1)}},
+ nodes_with_edges_only=True)
+ mark_const_producer_nodes(graph)
+ self.assertTrue((graph.node['node_6']['is_const_producer']))
+ self.assertListEqual(sorted(['node_1', 'node_2', 'node_3', 'node_5', 'placeholder_1']),
+ sorted(get_nodes_with_attributes(graph, is_const_producer=False, kind='op')))
+
+ graph_clean_up(graph)
+ self.assertTrue('node_3' in graph.nodes())
+ self.assertTrue('node_4' not in graph.nodes())
+ self.assertTrue('node_6' not in graph.nodes())
+
+ def test_undead_nodes_with_constant_inputs(self):
+ """
+ Checks that if node of 'undead' type has constant inputs it is not removed from the graph.
+ :return: None
+ """
+ pass
+
+ def test_remove_node_from_graph(self):
+ """
+ Checks case when remove node from graph.
+ The graph doesn't contain removed node yet.
+ "node_2" should be removed.
+
+ placeholder_1->node_1->node_2->node_3
+
+ :return: None
+ """
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'node_1'),
+ ('node_1', 'node_2'),
+ ('node_2', 'node_3')],
+ nodes_with_edges_only=True)
+ erase_node(Node(graph, 'node_2'))
+
+ self.assertListEqual(sorted(['placeholder_1', 'node_1', 'node_3']), sorted(graph.nodes()))
diff --git a/model-optimizer/mo/middle/passes/fusing/decomposition_test.py b/model-optimizer/mo/middle/passes/fusing/decomposition_test.py
new file mode 100644
index 000000000..2179f217c
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/fusing/decomposition_test.py
@@ -0,0 +1,531 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.middle.passes.eliminate import graph_clean_up
+from mo.middle.passes.fusing.decomposition import convert_scale_shift_to_mul_add, convert_batch_norm, \
+ convert_bn_to_mul_add
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_2': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ScaleShift layer
+ 'scaleshift_1': {'type': 'ScaleShift', 'kind': 'op', 'op': 'ScaleShift', 'axis': 0},
+ 'scaleshift_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Mul and Add operations
+ 'mul_1': {'type': None, 'value': None, 'kind': 'op', 'op': 'Mul'},
+ 'mul_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mul_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'add_1': {'type': None, 'kind': 'op', 'op': 'Add'},
+ 'add_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'add_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Mul and Add operations
+ 'mul_2': {'type': None, 'kind': 'op', 'op': 'Mul'},
+ 'mul_2_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mul_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'add_2': {'type': None, 'kind': 'op', 'op': 'Add'},
+ 'add_2_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'add_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Reshape
+ 'placeholder_2/Reshape_': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'placeholder_2/Reshape_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # BatchNorm operation
+ 'bn_op': {'type': None, 'kind': 'op', 'op': 'BatchNorm', 'can_be_fused': True},
+ 'bn_const': {'value': None, 'shape': None, 'kind': 'data'},
+ 'bn_beta': {'value': None, 'shape': None, 'kind': 'data'},
+ 'bn_mean': {'value': None, 'shape': None, 'kind': 'data'},
+ 'bn_var': {'value': None, 'shape': None, 'kind': 'data'},
+ 'bn_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Concat1 operation
+ 'concat': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+ 'concat_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class ScaleShiftToMulAdd(unittest.TestCase):
+ # ScaleShift -> Mul
+ def test_scaleshift_to_mul_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'mul_1': {'can_be_fused': True},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ convert_scale_shift_to_mul_add(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+ # ScaleShift 2 inputs-> Mul
+ def test_scaleshift2_to_mul(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('placeholder_2_data', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227])},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'placeholder_2/Reshape_'),
+ ('placeholder_2/Reshape_', 'placeholder_2/Reshape_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('placeholder_2/Reshape_data', 'mul_1'),
+ ('mul_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227])},
+ 'placeholder_2/Reshape_': {'dim': np.array([1, 227, 1, 1])},
+ 'placeholder_2/Reshape_data': {'shape': np.array([1, 227, 1, 1])},
+ 'mul_1': {'can_be_fused': True},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ convert_scale_shift_to_mul_add(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+ # ScaleShift 2 inputs-> Mul (axis = 1)
+ def test_scaleshift2_axis1_to_mul(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('placeholder_2_data', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([227])},
+ 'scaleshift_1': {'axis': 1},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'placeholder_2/Reshape_'),
+ ('placeholder_2/Reshape_', 'placeholder_2/Reshape_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('placeholder_2/Reshape_data', 'mul_1'),
+ ('mul_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([227])},
+ 'placeholder_2/Reshape_': {'dim': np.array([1, 227, 1, 1])},
+ 'placeholder_2/Reshape_data': {'shape': np.array([1, 227, 1, 1])},
+ 'mul_1': {'can_be_fused': True},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ convert_scale_shift_to_mul_add(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+
+ # ScaleShift -> Mul (Zero biases)
+ def test_scaleshift_to_mul_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([0, 0, 0])},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'mul_1': {'can_be_fused': True},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ convert_scale_shift_to_mul_add(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+ # ScaleShift -> Mul->Add
+ def test_scaleshift_to_mul_add(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([3, 2, 1])},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'add_1_w': {'shape': np.array([3]), 'value': np.array([3, 2, 1])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1': {'can_be_fused': True},
+ 'mul_1': {'can_be_fused': True},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ convert_scale_shift_to_mul_add(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+ # ScaleShift -> None (Zero weights and biases)
+ def test_scaleshift_to_nothing(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array([1, 1, 1])},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([0, 0, 0])},
+ 'scaleshift_1_data': {'shape': np.array([1, 227, 227, 3]), 'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3]), 'is_output': True}})
+
+ graph.graph['layout'] = 'NHWC'
+ convert_scale_shift_to_mul_add(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'placeholder_1_data')
+ self.assertTrue(flag, resp)
+
+ # ScaleShift -> ScaleShift (can_be_fused=False)
+ def test_scaleshift_can_be_fused(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array([1, 1, 1])},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([0, 0, 0])},
+ 'scaleshift_1': {'can_be_fused': False},
+ 'scaleshift_1_data': {'shape': np.array([1, 227, 227, 3]), 'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array([1, 1, 1])},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([0, 0, 0])},
+ 'scaleshift_1': {'can_be_fused': False},
+ 'scaleshift_1_data': {'shape': np.array([1, 227, 227, 3]), 'is_output': True}
+ })
+
+ convert_scale_shift_to_mul_add(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+
+class BatchNormDecomposition(unittest.TestCase):
+ def test_bn_decomposition_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'bn_op'),
+ ('bn_const', 'bn_op'),
+ ('bn_beta', 'bn_op'),
+ ('bn_mean', 'bn_op'),
+ ('bn_var', 'bn_op'),
+ ('bn_op', 'bn_data'),
+ ('concat', 'concat_data'),
+ ('bn_data', 'concat')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'bn_op': {'eps': 1.2},
+ 'bn_const': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_beta': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_mean': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_var': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_data': {'shape': np.array([1, 227, 227, 3])},
+ 'concat_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'add_2'),
+ ('add_2_w', 'add_2'),
+ ('add_2', 'add_2_data'),
+ ('concat', 'concat_data'),
+ ('add_2_data', 'concat')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]),
+ 'value': np.array([0.67419986, 0.55901699, 0.48795004])},
+ 'mul_2_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'add_1_w': {'shape': np.array([3]),
+ 'value': np.array([-0.67419986, -1.11803399, -1.46385011])},
+ 'add_2_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'add_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1': {'can_be_fused': True},
+ 'mul_2': {'can_be_fused': True},
+ 'add_1': {'can_be_fused': True},
+ 'add_2': {'can_be_fused': True},
+ 'concat_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ convert_batch_norm(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_data')
+ self.assertTrue(flag, resp)
+
+ # 'can_be_fused': False for BatchNorm
+ def test_bn_decomposition_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'bn_op'),
+ ('bn_const', 'bn_op'),
+ ('bn_beta', 'bn_op'),
+ ('bn_mean', 'bn_op'),
+ ('bn_var', 'bn_op'),
+ ('bn_op', 'bn_data'),
+ ('concat', 'concat_data'),
+ ('bn_data', 'concat')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'bn_op': {'eps': 1.2, 'can_be_fused': False},
+ 'bn_const': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_beta': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_mean': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_var': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_data': {'shape': np.array([1, 227, 227, 3])},
+ 'concat_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'add_2'),
+ ('add_2_w', 'add_2'),
+ ('add_2', 'add_2_data'),
+ ('concat', 'concat_data'),
+ ('add_2_data', 'concat')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]),
+ 'value': np.array([0.67419986, 0.55901699, 0.48795004])},
+ 'mul_2_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'add_1_w': {'shape': np.array([3]),
+ 'value': np.array([-0.67419986, -1.11803399, -1.46385011])},
+ 'add_2_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'add_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1': {'can_be_fused': False},
+ 'mul_2': {'can_be_fused': False},
+ 'add_1': {'can_be_fused': False},
+ 'add_2': {'can_be_fused': False},
+ 'concat_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ convert_batch_norm(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_data')
+ self.assertTrue(flag, resp)
+
+ def test_caffe_bn_decomposition_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'bn_op'),
+ ('bn_mean', 'bn_op'),
+ ('bn_var', 'bn_op'),
+ ('bn_op', 'bn_data'),
+ ('concat', 'concat_data'),
+ ('bn_data', 'concat')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'bn_op': {'epsilon': 1.2, 'op': 'BatchNormalization'},
+ 'bn_mean': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_var': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_data': {'shape': np.array([1, 227, 227, 3])},
+ 'concat_data': {'is_output': True}
+ })
+
+ del graph['placeholder_1']['placeholder_1_data'][0]['in']
+ del graph['bn_op']['bn_data'][0]['in']
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('concat', 'concat_data'),
+ ('add_1_data', 'concat')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]),
+ 'value': np.array([0.67419986, 0.55901699, 0.48795004])},
+ 'add_1_w': {'shape': np.array([3]),
+ 'value': np.array([-0.67419986, -1.11803399, -1.46385011])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1': {'can_be_fused': True},
+ 'add_1': {'can_be_fused': True},
+ 'concat_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ convert_bn_to_mul_add(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_data')
+ self.assertTrue(flag, resp)
+
+ # 'can_be_fused': False for BatchNormalization
+ def test_caffe_bn_decomposition_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'bn_op'),
+ ('bn_mean', 'bn_op'),
+ ('bn_var', 'bn_op'),
+ ('bn_op', 'bn_data'),
+ ('concat', 'concat_data'),
+ ('bn_data', 'concat')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'bn_op': {'epsilon': 1.2, 'op': 'BatchNormalization', 'can_be_fused': False},
+ 'bn_mean': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_var': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'bn_data': {'shape': np.array([1, 227, 227, 3])},
+ 'concat_data': {'is_output': True}
+ })
+
+ del graph['placeholder_1']['placeholder_1_data'][0]['in']
+ del graph['bn_op']['bn_data'][0]['in']
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('concat', 'concat_data'),
+ ('add_1_data', 'concat')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]),
+ 'value': np.array([0.67419986, 0.55901699, 0.48795004])},
+ 'add_1_w': {'shape': np.array([3]),
+ 'value': np.array([-0.67419986, -1.11803399, -1.46385011])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1': {'can_be_fused': False},
+ 'add_1': {'can_be_fused': False},
+ 'concat_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+ convert_bn_to_mul_add(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_data')
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/mo/middle/passes/fusing/fuse_linear_ops_test.py b/model-optimizer/mo/middle/passes/fusing/fuse_linear_ops_test.py
new file mode 100644
index 000000000..30948e25e
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/fusing/fuse_linear_ops_test.py
@@ -0,0 +1,1201 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.middle.passes.eliminate import graph_clean_up
+from mo.middle.passes.fusing.fuse_linear_ops import _fuse_mul, _fuse_add, fuse_linear_ops
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ScaleShift layer
+ 'scaleshift_1': {'type': 'ScaleShift', 'kind': 'op', 'op': 'ScaleShift'},
+ 'scaleshift_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Mul and Add operations
+ 'mul_1': {'type': 'Mul', 'kind': 'op', 'op': 'Mul', 'can_be_fused': True},
+ 'mul_1_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_1': {'type': 'Add', 'kind': 'op', 'op': 'Add', 'can_be_fused': True},
+ 'add_1_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Mul2 and Add2 operations
+ 'mul_2': {'type': 'Mul', 'kind': 'op', 'op': 'Mul', 'can_be_fused': True},
+ 'mul_2_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_2': {'type': 'Add', 'kind': 'op', 'op': 'Add', 'can_be_fused': True},
+ 'add_2_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Concat1 operation
+ 'concat_1': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+ 'concat_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Convolutions
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NHWC'},
+ 'conv_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NHWC'},
+ 'conv_2_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # FullyConnected
+ 'fc_1': {'type': 'FullyConnected', 'kind': 'op', 'op': 'InnerProduct', 'layout': 'NHWC'},
+ 'fc_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Placeholders
+ 'placeholder_2': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_3': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+}
+
+
+# Unit tests for fuse_mul
+class FuseMulTests(unittest.TestCase):
+ # Mul(array)->Conv(w+b)
+ def test_fuse_mul_to_conv_1(self):
+ # Placeholder->Mul->Conv
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'is_output': True}
+ })
+ ref_weights = np.ones((11, 11, 3, 96)) * np.reshape(np.array([1, 2, 3]), (3, 1))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'is_output': True}
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_1_data')
+ self.assertTrue(flag, resp)
+
+ # Mul(scalar)->Conv(w+b)
+ def test_fuse_mul_to_conv_2(self):
+ # Placeholder->Mul->Conv
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'is_output': True}
+ })
+ ref_weights = np.ones((11, 11, 3, 96)) * np.reshape(np.array([6, 6, 6]), (3, 1))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'is_output': True}
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_1_data')
+ self.assertTrue(flag, resp)
+
+ # Conv(w+b)->Mul(array)
+ def test_fuse_mul_to_conv_3(self):
+ # Placeholder->Conv->Mul
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.ones(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'mul_1_data': {'shape': np.array([1, 55, 55, 96]), 'is_output': True},
+ 'mul_1_w': {'shape': np.array([96]), 'value': np.array([x for x in range(96)])},
+ })
+ ref_weights = np.ones((11, 11, 3, 96)) * np.reshape(np.array([x for x in range(96)]), 96)
+ ref_biases = np.ones(96) * np.array([x for x in range(96)])
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': ref_biases.shape, 'value': ref_biases},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96]), 'is_output': True}
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1')], backward=True)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'mul_1_data', 'conv_1_data')
+ self.assertTrue(flag, resp)
+
+ # Conv(w)->Mul(scalar)
+ def test_fuse_mul_to_conv_4(self):
+ # Placeholder->Conv->Mul
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.ones(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'mul_1_data': {'shape': np.array([1, 55, 55, 96]), 'is_output': True},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ })
+ ref_weights = np.ones((11, 11, 3, 96)) * np.array([6])
+ ref_biases = np.ones(96) * np.array([6])
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': ref_biases.shape, 'value': ref_biases},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96]), 'is_output': True}
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1')], backward=True)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'mul_1_data', 'conv_1_data')
+ self.assertTrue(flag, resp)
+
+ # Op0-+->Op1--+----+-->Concat Op0-+->Op1--+--+-->Concat
+ # | | | | | | | |
+ # | +->Op2--+ | => | +->Op2--+ |
+ # +---->Mul->Conv-+ +---->Conv----+
+ def test_fuse_mul_to_conv_5(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('placeholder_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_1_data', 'placeholder_3'),
+ ('placeholder_3', 'placeholder_3_data'),
+ ('placeholder_2_data', 'concat_1'),
+ ('placeholder_3_data', 'concat_1'),
+ ('conv_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'concat_1_data': {'is_output': True}
+ })
+ ref_weights = np.ones((11, 11, 3, 96)) * np.reshape(np.array([6, 6, 6]), (3, 1))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('placeholder_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_1_data', 'placeholder_3'),
+ ('placeholder_3', 'placeholder_3_data'),
+ ('placeholder_2_data', 'concat_1'),
+ ('placeholder_3_data', 'concat_1'),
+ ('conv_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3,
+ 'input_channel_dim': 2, 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'is_output': True},
+ 'placeholder_2_data': {'is_output': True},
+ 'placeholder_3_data': {'is_output': True},
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ def test_fuse_mul_to_conv_5_nparray(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('placeholder_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_1_data', 'placeholder_3'),
+ ('placeholder_3', 'placeholder_3_data'),
+ ('placeholder_2_data', 'concat_1'),
+ ('placeholder_3_data', 'concat_1'),
+ ('conv_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': np.array([6])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'concat_1_data': {'is_output': True}
+ })
+ ref_weights = np.ones((11, 11, 3, 96)) * np.reshape(np.array([6, 6, 6]), (3, 1))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('placeholder_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_1_data', 'placeholder_3'),
+ ('placeholder_3', 'placeholder_3_data'),
+ ('placeholder_2_data', 'concat_1'),
+ ('placeholder_3_data', 'concat_1'),
+ ('conv_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3,
+ 'input_channel_dim': 2, 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'is_output': True},
+ 'placeholder_2_data': {'is_output': True},
+ 'placeholder_3_data': {'is_output': True},
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Op->Mul(array)-+->Conv(w+b)--+->Concat Op-+->Conv1-+-->Concat
+ # | | => | |
+ # +-->Conv(w+b)-+ +->Conv2-+
+ def test_fuse_mul_to_convolutions_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('mul_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_1_data', 'concat_1'),
+ ('conv_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'conv_2_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_2_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_2_data': {'shape': np.array([1, 55, 55, 96])},
+ 'concat_1_data': {'is_output': True}
+ })
+ ref_weights = np.ones((11, 11, 3, 96)) * np.reshape(np.array([1, 2, 3]), (3, 1))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('placeholder_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_1_data', 'concat_1'),
+ ('conv_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'conv_2_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_2_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_2_data': {'shape': np.array([1, 55, 55, 96])},
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1'), Node(graph, 'conv_2')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Mul(array)->FC(w+b)
+ def test_fuse_mul_to_fc_1(self):
+ # Placeholder->Mul->FC
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'mul_1_data': {'shape': np.array([1, 2048])},
+ 'mul_1_w': {'shape': np.array([2048]), 'value': np.array([x for x in range(2048)])},
+ 'fc_1_w': {'shape': np.array([10260, 2048]), 'value': np.ones((10260, 2048)),
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': np.array([10260]), 'value': np.ones(10260)},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+ ref_weights = np.ones((10260, 2048)) * np.array([x for x in range(2048)])
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'fc_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': np.array([10260]), 'value': np.ones(10260)},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'fc_1')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'fc_1_data')
+ self.assertTrue(flag, resp)
+
+ # Mul(scalar)->Conv(w+b) can_be_fused = False
+ def test_fuse_mul_to_conv_6(self):
+ # Placeholder->Mul->Conv
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'conv_1': {'can_be_fused': False},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'conv_1': {'can_be_fused': False},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'is_output': True}
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_1_data')
+ self.assertTrue(flag, resp)
+
+ # Mul(array)->DWConv(w+b)
+ def test_fuse_mul_to_dwconv_1(self):
+ # Placeholder->Mul->Conv
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 112, 112, 6])},
+ 'mul_1_data': {'shape': np.array([1, 112, 112, 6])},
+ 'mul_1_w': {'shape': np.array([6]), 'value': np.array([1, 2, 3, 4, 5, 6])},
+ 'conv_1_w': {'shape': np.array([3, 3, 6, 1]), 'value': np.ones((3, 3, 6, 1)),
+ 'output_channel_dim': 2, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_data': {'is_output': True}
+ })
+ ref_weights = np.ones((3, 3, 6, 1)) * np.reshape(np.array([1, 2, 3, 4, 5, 6]), (6, 1))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 112, 112, 6])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 2, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_data': {'is_output': True}
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_1_data')
+ self.assertTrue(flag, resp)
+
+ # DWConv(w)->Mul(scalar)
+ def test_fuse_mul_to_dwconv_2(self):
+ # Placeholder->Conv->Mul
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 112, 112, 6])},
+ 'mul_1_data': {'shape': np.array([1, 112, 112, 6]), 'is_output': True},
+ 'mul_1_w': {'shape': np.array([6]), 'value': np.array([1, 2, 3, 4, 5, 6])},
+ 'conv_1_w': {'shape': np.array([3, 3, 6, 1]), 'value': np.ones((3, 3, 6, 1)),
+ 'output_channel_dim': 2, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_data': {'is_output': True}
+ })
+
+ ref_weights = np.ones((3, 3, 6, 1)) * np.reshape(np.array([1, 2, 3, 4, 5, 6]), (6, 1))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 112, 112, 6])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 2, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ })
+
+ _fuse_mul(graph, Node(graph, 'mul_1'), [Node(graph, 'conv_1')], backward=True)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'mul_1_data', 'conv_1_data')
+ self.assertTrue(flag, resp)
+
+
+# Unit tests for fuse_add
+class FuseAddTests(unittest.TestCase):
+ # Add(array)->FC(w+b)
+ def test_fuse_add_to_fc_1(self):
+ # Placeholder->Add->FC
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'add_1_data': {'shape': np.array([1, 2048])},
+ 'add_1_w': {'shape': np.array([2048]), 'value': np.array([x for x in range(2048)])},
+ 'fc_1_w': {'shape': np.array([10260, 2048]), 'value': np.ones((10260, 2048)),
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': np.array([10260]), 'value': np.ones(10260)},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+ ref_weights = np.ones((10260, 2048))
+ ref_biases = np.ones(10260) + np.dot(np.ones((10260, 2048)), np.array([x for x in range(2048)]))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'fc_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': ref_biases.shape, 'value': ref_biases},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+
+ _fuse_add(graph, Node(graph, 'add_1'), [Node(graph, 'fc_1')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'fc_1_data')
+ self.assertTrue(flag, resp)
+
+ # FC(w)->Add(array)
+ def test_fuse_add_to_fc_2(self):
+ # Placeholder->FC->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ('fc_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'add_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ 'add_1_w': {'shape': np.array([10260]), 'value': np.array([x for x in range(10260)]),
+ 'data_type': None},
+ 'fc_1_w': {'shape': np.array([10260, 2048]), 'value': np.ones((10260, 2048)),
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2, 'data_type': None},
+ 'fc_1_data': {'shape': np.array([1, 10260])},
+ })
+
+ ref_weights = np.ones((10260, 2048))
+ ref_biases = np.array([x for x in range(10260)])
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'fc_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': ref_biases.shape, 'value': ref_biases},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+
+ _fuse_add(graph, Node(graph, 'add_1'), [Node(graph, 'fc_1')], backward=True)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'fc_1_data')
+ self.assertTrue(flag, resp)
+
+ # FC(w)->Add(scalar)
+ def test_fuse_add_to_fc_3(self):
+ # Placeholder->FC->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ('fc_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'add_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6, 'data_type': None},
+ 'fc_1_w': {'shape': np.array([10260, 2048]), 'value': np.ones((10260, 2048)),
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2, 'data_type': None},
+ 'fc_1_data': {'shape': np.array([1, 10260])},
+ })
+
+ ref_weights = np.ones((10260, 2048))
+ ref_biases = np.full([10260], 6)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'fc_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': ref_biases.shape, 'value': ref_biases},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+
+ _fuse_add(graph, Node(graph, 'add_1'), [Node(graph, 'fc_1')], backward=True)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'fc_1_data')
+ self.assertTrue(flag, resp)
+
+ # Add(array)->FC(w+b) can_be_fused = False
+ def test_fuse_add_to_fc_4(self):
+ # Placeholder->Add->FC
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'add_1_data': {'shape': np.array([1, 2048])},
+ 'add_1_w': {'shape': np.array([2048]), 'value': np.array([x for x in range(2048)])},
+ 'fc_1': {'can_be_fused': False},
+ 'fc_1_w': {'shape': np.array([10260, 2048]), 'value': np.ones((10260, 2048)),
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': np.array([10260]), 'value': np.ones(10260)},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'add_1_data': {'shape': np.array([1, 2048])},
+ 'add_1_w': {'shape': np.array([2048]), 'value': np.array([x for x in range(2048)])},
+ 'fc_1': {'can_be_fused': False},
+ 'fc_1_w': {'shape': np.array([10260, 2048]), 'value': np.ones((10260, 2048)),
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': np.array([10260]), 'value': np.ones(10260)},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+
+ _fuse_add(graph, Node(graph, 'add_1'), [Node(graph, 'fc_1')], backward=False)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'fc_1_data')
+ self.assertTrue(flag, resp)
+
+
+# Unit tests for fuse_linear_ops
+class FuseLinOpsTests(unittest.TestCase):
+ # Op->Mul(array)-+->Conv(w+b)->Add-+->Concat Op-+->Conv1-+-->Concat
+ # | | => | |
+ # +-->Conv(w+b)-----+ +->Conv2-+
+ def test_fuse_lin_ops_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('mul_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_1_data', 'concat_1'),
+ ('conv_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'conv_2_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_2_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_2_data': {'shape': np.array([1, 55, 55, 96])},
+ 'concat_1_data': {'is_output': True}
+ })
+ ref_weights = np.ones((11, 11, 3, 96)) * np.reshape(np.array([1, 2, 3]), (3, 1))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('placeholder_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_1_data', 'concat_1'),
+ ('conv_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'conv_2_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_2_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_2_data': {'shape': np.array([1, 55, 55, 96])},
+ })
+
+ fuse_linear_ops(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Mul(array)->FC(w+b)
+ def test_fuse_mul_to_fc_1(self):
+ # Placeholder->Mul->FC
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'mul_1_data': {'shape': np.array([1, 2048])},
+ 'mul_1_w': {'shape': np.array([2048]), 'value': np.array([x for x in range(2048)])},
+ 'fc_1_w': {'shape': np.array([10260, 2048]), 'value': np.ones((10260, 2048)),
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': np.array([10260]), 'value': np.ones(10260)},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+ ref_weights = np.ones((10260, 2048)) * np.array([x for x in range(2048)])
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'fc_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': np.array([10260]), 'value': np.ones(10260)},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+
+ fuse_linear_ops(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'fc_1_data')
+ self.assertTrue(flag, resp)
+
+ # FC(w)->Add(scalar)
+ def test_fuse_add_to_fc_3(self):
+ # Placeholder->FC->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ('fc_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'add_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ 'add_1_w': {'shape': np.array([1]), 'value': np.array([6]), 'data_type': None},
+ 'fc_1_w': {'shape': np.array([10260, 2048]), 'value': np.ones((10260, 2048)),
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2, 'data_type': None},
+ 'fc_1_data': {'shape': np.array([1, 10260])},
+ })
+
+ ref_weights = np.ones((10260, 2048))
+ ref_biases = np.array([6 for x in range(10260)])
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'fc_1'),
+ ('fc_1_w', 'fc_1'),
+ ('fc_1_b', 'fc_1'),
+ ('fc_1', 'fc_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 2048])},
+ 'fc_1_w': {'shape': ref_weights.shape, 'value': ref_weights,
+ 'output_channel_dim': 0, 'input_channel_dim': 1,
+ 'dims_number': 2},
+ 'fc_1_b': {'shape': ref_biases.shape, 'value': ref_biases},
+ 'fc_1_data': {'shape': np.array([1, 10260]), 'is_output': True},
+ })
+
+ fuse_linear_ops(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'add_1_data', 'fc_1_data')
+ self.assertTrue(flag, resp)
+
+ # +-----------+
+ # | | => Same
+ # Placeholder--->Add->Mul-----+->Concat
+ def test_fuse_lin_op_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('add_1_data', 'concat_1'),
+ ('mul_1_data', 'concat_1'),
+ ('add_1_data', 'mul_1')],
+
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': np.array([1, 1, 3, 3]), 'value': np.zeros((1, 1, 3, 3)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([3]), 'value': np.zeros(3)},
+ 'conv_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': np.array([6])},
+ 'add_1_w': {'shape': np.array([1]), 'value': np.array([1])},
+ 'concat_1_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1_data', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1_data', 'concat_1'),
+ ('mul_1_w', 'mul_1'),
+ ('conv_1_data', 'mul_1'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'concat_1')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'conv_1_w': {'shape': np.array([1, 1, 3, 3]), 'value': np.zeros((1, 1, 3, 3)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([3]), 'value': np.ones(3)},
+ 'conv_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': np.array([6])},
+ 'concat_1_data': {'is_output': True}
+ })
+
+ fuse_linear_ops(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Op->Mul(array)-+->Conv(w+b)------+->Concat
+ # | | => Same('can_be_fused': False)
+ # +-->Conv(w+b)-----+
+ def test_fuse_lin_ops_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('mul_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_1_data', 'concat_1'),
+ ('conv_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'conv_2': {'can_be_fused': False},
+ 'conv_2_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_2_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_2_data': {'shape': np.array([1, 55, 55, 96])},
+ 'concat_1_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('mul_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_1_data', 'concat_1'),
+ ('conv_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'conv_2': {'can_be_fused': False},
+ 'conv_2_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_2_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_2_data': {'shape': np.array([1, 55, 55, 96])},
+ 'concat_1_data': {'is_output': True}
+ })
+
+ fuse_linear_ops(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Op->Mul(array)-+->Conv(w+b)------+->Concat
+ # | | => Same('can_be_fused': False)
+ # +-->Conv(w+b)-----+
+ def test_fuse_lin_ops_3(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('mul_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_1_data', 'concat_1'),
+ ('conv_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1': {'can_be_fused': False},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'conv_2_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_2_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_2_data': {'shape': np.array([1, 55, 55, 96])},
+ 'concat_1_data': {'is_output': True}
+ })
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('mul_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_1_data', 'concat_1'),
+ ('conv_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1': {'can_be_fused': False},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'conv_1_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_1_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_1_data': {'shape': np.array([1, 55, 55, 96])},
+ 'conv_2_w': {'shape': np.array([11, 11, 3, 96]), 'value': np.ones((11, 11, 3, 96)),
+ 'output_channel_dim': 3, 'input_channel_dim': 2,
+ 'dims_number': 4},
+ 'conv_2_b': {'shape': np.array([96]), 'value': np.zeros(96)},
+ 'conv_2_data': {'shape': np.array([1, 55, 55, 96])},
+ 'concat_1_data': {'is_output': True}
+ })
+
+ fuse_linear_ops(graph)
+ graph_clean_up(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/mo/middle/passes/fusing/fuse_linear_seq_test.py b/model-optimizer/mo/middle/passes/fusing/fuse_linear_seq_test.py
new file mode 100644
index 000000000..d320b575a
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/fusing/fuse_linear_seq_test.py
@@ -0,0 +1,908 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.middle.passes.fusing.fuse_linear_seq import fuse_mul_add_sequence
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ScaleShift layer
+ 'scaleshift_1': {'type': 'ScaleShift', 'kind': 'op', 'op': 'ScaleShift'},
+ 'scaleshift_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Mul and Add operations
+ 'mul_1': {'type': 'Mul', 'kind': 'op', 'op': 'Mul', 'can_be_fused': True},
+ 'mul_1_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_1': {'type': 'Add', 'kind': 'op', 'op': 'Add', 'can_be_fused': True},
+ 'add_1_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Mul2 and Add2 operations
+ 'mul_2': {'type': 'Mul', 'kind': 'op', 'op': 'Mul', 'can_be_fused': True},
+ 'mul_2_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_2': {'type': 'Add', 'kind': 'op', 'op': 'Add', 'can_be_fused': True},
+ 'add_2_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Mul3 and Add3 operations
+ 'mul_3': {'type': 'Mul', 'kind': 'op', 'op': 'Mul', 'can_be_fused': True},
+ 'mul_3_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_3': {'type': 'Add', 'kind': 'op', 'op': 'Add', 'can_be_fused': True},
+ 'add_3_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Mul4 and Add4 operations
+ 'mul_4': {'type': 'Mul', 'kind': 'op', 'op': 'Mul', 'can_be_fused': True},
+ 'mul_4_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_4_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_4': {'type': 'Add', 'kind': 'op', 'op': 'Add', 'can_be_fused': True},
+ 'add_4_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_4_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Concat1 operation
+ 'concat_1': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+ 'concat_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Convolutions
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NHWC'},
+ 'conv_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NHWC'},
+ 'conv_2_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # FullyConnected
+ 'fc_1': {'type': 'FullyConnected', 'kind': 'op', 'op': 'InnerProduct', 'layout': 'NHWC'},
+ 'fc_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Placeholders
+ 'placeholder_2': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_3': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+}
+
+
+# Unit tests for fuse_mul_add_sequence
+class LinSeqFusingTests(unittest.TestCase):
+ # Placeholder-+->Mul->Add->Mul-+->Concat
+ # | |
+ # +----------------+
+ def test_fuse_lin_seq_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'add_1_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'mul_1': {'can_be_fused': True},
+ 'add_1': {'can_be_fused': True},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # +----------------+
+ # | |
+ # Placeholder-+->Mul->Add->Mul-+---------------+->Concat
+ # | |
+ # +-->Placeholder----+
+ def test_fuse_lin_seq_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ('mul_2_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ('add_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'add_1_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'mul_1': {'can_be_fused': True},
+ 'add_1': {'can_be_fused': True},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # +----->Placeholder
+ # | | => The same graph
+ # Placeholder--->Mul->Add->Mul--+->Concat
+ def test_fuse_lin_seq_3(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('add_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('add_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # +-------->Placeholder +-------->Placeholder
+ # | | => | |
+ # Placeholder--->Mul->Add->Mul-+->Concat Placeholder-+->Mul->Mul->Add-+->Concat
+ def test_fuse_lin_seq_4(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'mul_2_w': {'shape': np.array([1]), 'value': np.array([6])},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # +-------->Placeholder +->Placeholder
+ # | | => | |
+ # Placeholder--->Mul->Add->Mul-+->Concat Placeholder--->Mul-----------+->Concat
+ def test_fuse_lin_seq_5(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 0},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 1},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # +-------->Placeholder +->Placeholder
+ # | | => | |
+ # Placeholder--->Mul->Add->Mul-+->Concat Placeholder--->Mul-->Add-----+->Concat
+ def test_fuse_lin_seq_6(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 1},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': np.array([6])},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # +-------->Placeholder +->Placeholder
+ # | | => | |
+ # Placeholder--->Mul->Add->Mul-+->Concat Placeholder--->Mul-->Mul-----+->Concat
+ def test_fuse_lin_seq_7(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 0},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('mul_1_data', 'placeholder_2'),
+ ('placeholder_2', 'placeholder_2_data'),
+ ('placeholder_2_data', 'concat_1')
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'placeholder_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': np.array([6])},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Placeholder--->Mul->Add->Mul-+->Concat Placeholder->Concat
+ def test_fuse_lin_seq_8(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 1},
+ 'add_1_w': {'shape': np.array([1]), 'value': 0},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 1},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Placeholder--->Mul->Add->Mul-+->Concat Placeholder->Mul->Add->Concat
+ def test_fuse_lin_seq_9(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'add_1_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Placeholder--->Mul->Add->Mul-+->Concat Placeholder->Mul->Add->Concat
+ def test_fuse_lin_seq_10(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([3]), 'value': np.array([6, 6, 6])},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([36, 36, 36])},
+ 'add_1_w': {'shape': np.array([3]), 'value': np.array([36, 36, 36])},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Placeholder-+->Mul->Add->Mul-+->Concat
+ # | | With 'can_be_fused' = False
+ # +----------------+
+ def test_fuse_lin_seq_11(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_1': {'can_be_fused': False},
+ 'add_1': {'can_be_fused': False},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_1': {'can_be_fused': False},
+ 'add_1': {'can_be_fused': False},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Placeholder-+->Mul->Add->Mul-+->Concat
+ # | | With 'can_be_fused' = False
+ # +----------------+
+ def test_fuse_lin_seq_12(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1': {'can_be_fused': False},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1': {'can_be_fused': False},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node), len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
+
+ # Placeholder-+->Mul->Add->Mul-+->Concat
+ # | |
+ # +->Mul->Mul->----+ (This Mul ops has shared weights with upper Mul ops)
+ def test_fuse_lin_seq_shared_weights_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'mul_3'),
+ ('mul_3', 'mul_3_data'),
+ ('mul_1_w', 'mul_3'),
+ ('mul_3_data', 'mul_4'),
+ ('mul_2_w', 'mul_4'),
+ ('mul_4', 'mul_4_data'),
+ ('mul_4_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_3_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_4_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'mul_3'),
+ ('mul_3', 'mul_3_data'),
+ ('mul_3_w', 'mul_3'),
+ ('mul_3_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_3_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'mul_3_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'add_1_w': {'shape': np.array([1]), 'value': np.array([36])},
+ 'mul_1': {'can_be_fused': True},
+ 'add_1': {'can_be_fused': True},
+ 'concat_1_data': {'is_output': True}
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NHWC'
+ fuse_mul_add_sequence(graph)
+ self.assertTrue(len(graph.node) == len(graph_ref.node),
+ "Graphs has different number of nodes: {} and {}".format(len(graph.node),
+ len(graph_ref.node)))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/mo/middle/passes/fusing/helpers_test.py b/model-optimizer/mo/middle/passes/fusing/helpers_test.py
new file mode 100644
index 000000000..feb2020e7
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/fusing/helpers_test.py
@@ -0,0 +1,307 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.graph.graph import Node
+from mo.middle.passes.fusing.helpers import forward_bfs, backward_bfs, get_next_operation
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ScaleShift layer
+ 'scaleshift_1': {'type': 'ScaleShift', 'kind': 'op', 'op': 'ScaleShift'},
+ 'scaleshift_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Mul and Add operations
+ 'mul_1': {'type': 'Mul', 'kind': 'op', 'op': 'Mul'},
+ 'mul_1_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_1': {'type': 'Add', 'kind': 'op', 'op': 'Add'},
+ 'add_1_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Mul2 and Add2 operations
+ 'mul_2': {'type': 'Mul', 'kind': 'op', 'op': 'Mul'},
+ 'mul_2_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_2': {'type': 'Add', 'kind': 'op', 'op': 'Add'},
+ 'add_2_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Concat1 operation
+ 'concat_1': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+ 'concat_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Convolutions
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NHWC'},
+ 'conv_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NHWC'},
+ 'conv_2_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # FullyConnected
+ 'fc_1': {'type': 'FullyConnected', 'kind': 'op', 'op': 'InnerProduct', 'layout': 'NHWC'},
+ 'fc_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Placeholders
+ 'placeholder_2': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_3': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+}
+
+
+# Unit tests for forward and backward bfs (forward_bfs, backward_bfs)
+class BFSTests(unittest.TestCase):
+ def test_forward_bfs_simple(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_data', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1', 'add_1_data')
+ ],
+ {'add_1_data': {'is_output': True}})
+
+ res = forward_bfs(Node(graph, 'placeholder_1'), ['ScaleShift', 'Mul'], ['Add'])
+ self.assertTrue(len(res) == 1 and res[0].id == 'add_1', 'Add operation was not found by bfs')
+
+ res = forward_bfs(Node(graph, 'placeholder_1'), [], ['Add'], allowed_all=True)
+ self.assertTrue(len(res) == 1 and res[0].id == 'add_1', 'Add operation was not found by bfs')
+
+ res = forward_bfs(Node(graph, 'placeholder_1_data'), ['ScaleShift'], ['Add'])
+ self.assertTrue(len(res) == 0, 'No one node should be found! But bfs found {} nodes'.format(len(res)))
+
+ res = forward_bfs(Node(graph, 'placeholder_1_data'), ['ScaleShift'], ['Mul', 'Add'])
+ self.assertTrue(len(res) == 1 and res[0].id == 'mul_1', 'BFS should find only one Mul operation')
+
+ def test_backward_bfs_simple(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_data', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1', 'add_1_data')
+ ],
+ {'add_1_data': {'is_output': True}})
+
+ res = backward_bfs(Node(graph, 'add_1_data'), ['Add', 'ScaleShift', 'Mul'], ['Placeholder'])
+ self.assertTrue(len(res) == 1 and res[0].id == 'placeholder_1', 'Placeholder operation was not found by bfs')
+
+ res = backward_bfs(Node(graph, 'add_1'), [], ['Placeholder'], allowed_all=True)
+ self.assertTrue(len(res) == 1 and res[0].id == 'placeholder_1', 'Placeholder operation was not found by bfs')
+
+ res = backward_bfs(Node(graph, 'add_1_data'), ['Add'], ['ScaleShift'])
+ self.assertTrue(len(res) == 0, 'No one node should be found! But bfs found {} nodes'.format(len(res)))
+
+ res = backward_bfs(Node(graph, 'add_1_data'), ['Add', 'Mul'], ['Placeholder', 'ScaleShift'])
+ self.assertTrue(len(res) == 1 and res[0].id == 'scaleshift_1', 'BFS should find only one ScaleShift operation')
+
+ def test_forward_bfs_hard(self):
+ # Placeholder->ScaleShift->Mul1->Add1---->Concat
+ # `----------->Add2->Mul2--'
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('placeholder_1_data', 'add_2'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_data', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_2', 'add_2_data'),
+ ('add_2_data', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('add_1_data', 'concat_1'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'concat_1_data': {'is_output': True}})
+
+ res = forward_bfs(Node(graph, 'placeholder_1'), ['ScaleShift', 'Mul', 'Add'], ['Concat'])
+ self.assertTrue(len(res) == 1 and res[0].id == 'concat_1', 'Probably Concat operation was not found by bfs')
+
+ res = forward_bfs(Node(graph, 'placeholder_1'), ['ScaleShift', 'Mul'], ['Add'])
+ self.assertTrue(len(res) == 2 and all([res[x].id in ['add_1', 'add_2'] for x in range(len(res))]),
+ 'Add operations was not found by bfs')
+
+ res = forward_bfs(Node(graph, 'placeholder_1'), ['ScaleShift'], ['Add'])
+ self.assertTrue(len(res) == 0, 'BFS shouldn\'t find any operations')
+
+ res = forward_bfs(Node(graph, 'placeholder_1'), [], ['Add'], allowed_all=True)
+ self.assertTrue(len(res) == 2 and all([res[x].id in ['add_1', 'add_2'] for x in range(len(res))]),
+ 'Add operations was not found by bfs')
+
+ res = forward_bfs(Node(graph, 'placeholder_1_data'), ['ScaleShift'], ['Concat'])
+ self.assertTrue(len(res) == 0, 'No one node should be found! But bfs found {} nodes'.format(len(res)))
+
+ def test_backward_bfs_hard(self):
+ # Placeholder->ScaleShift->Mul1->Add1---->Concat
+ # `----------->Add2->Mul2--'
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('placeholder_1_data', 'add_2'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_data', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_2', 'add_2_data'),
+ ('add_2_data', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('add_1_data', 'concat_1'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'concat_1_data': {'is_output': True}})
+
+ res = backward_bfs(Node(graph, 'concat_1'), ['ScaleShift', 'Mul', 'Add'], ['Placeholder'])
+ self.assertTrue(len(res) == 0, 'Smth went wrong with bfs')
+
+ res = backward_bfs(Node(graph, 'concat_1'), ['Mul'], ['Add'])
+ self.assertTrue(len(res) == 2 and all([res[x].id in ['add_1', 'add_2'] for x in range(len(res))]),
+ 'Add operations was not found by bfs')
+
+ res = backward_bfs(Node(graph, 'concat_1'), ['ScaleShift'], ['Add'])
+ self.assertTrue(len(res) == 0, 'BFS shouldn\'t find any operations')
+
+ res = backward_bfs(Node(graph, 'concat_1'), [], ['Add'], allowed_all=True)
+ self.assertTrue(len(res) == 2 and all([res[x].id in ['add_1', 'add_2'] for x in range(len(res))]),
+ 'Add operations was not found by bfs')
+
+ res = backward_bfs(Node(graph, 'concat_1'), ['ScaleShift'], ['ScaleShift'])
+ self.assertTrue(len(res) == 0, 'No one node should be found! But bfs found {} nodes'.format(len(res)))
+
+ def test_backward_bfs_hard2(self):
+ # Placeholder->ScaleShift->Mul1->Add1---->Concat
+ # `----------->Add2->Mul2--'
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'add_2'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_data', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_2', 'add_2_data'),
+ ('add_2_data', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('add_1_data', 'concat_1'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'concat_1_data': {'is_output': True}})
+
+ res = backward_bfs(Node(graph, 'concat_1'), ['Mul', 'Add'], ['Placeholder'])
+ self.assertTrue(len(res) == 0, 'Smth went wrong with bfs')
+
+ res = backward_bfs(Node(graph, 'concat_1'), ['Mul'], ['Add'])
+ self.assertTrue(len(res) == 2 and all([res[x].id in ['add_1', 'add_2'] for x in range(len(res))]),
+ 'Add operations was not found by bfs')
+
+ res = backward_bfs(Node(graph, 'concat_1'), ['ScaleShift'], ['Add'])
+ self.assertTrue(len(res) == 0, 'BFS shouldn\'t find any operations')
+
+ res = backward_bfs(Node(graph, 'concat_1'), [], ['Add'], allowed_all=True)
+ self.assertTrue(len(res) == 2 and all([res[x].id in ['add_1', 'add_2'] for x in range(len(res))]),
+ 'Add operations was not found by bfs')
+
+ res = backward_bfs(Node(graph, 'concat_1'), ['ScaleShift'], ['ScaleShift'])
+ self.assertTrue(len(res) == 0, 'No one node should be found! But bfs found {} nodes'.format(len(res)))
+
+ def test_backward_bfs_cycle(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_data', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'placeholder_1')
+ ],
+ {'add_1_data': {'is_output': True}})
+
+ res = backward_bfs(Node(graph, 'add_1_data'), ['Add', 'ScaleShift', 'Mul', 'Placeholder'], ['Conv2D'])
+ self.assertTrue(len(res) == 0, 'Sholdn\'t find any nodes due to cycle in graph')
+
+
+# Unit tests for get_next_operation
+class GetNextOperationTests(unittest.TestCase):
+ def test_get_next_operation_1(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_data', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1', 'add_1_data')
+ ],
+ {'add_1_data': {'is_output': True}})
+
+ res = get_next_operation(Node(graph, 'mul_1'))
+ self.assertTrue(len(res) == 1 and res[0].id == 'add_1', 'get_nex_operation returned wrong op')
+
+ def test_get_next_operation_2(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('placeholder_1_data', 'add_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1', 'add_1_data')
+ ],
+ {'add_1_data': {'is_output': True}})
+
+ res = get_next_operation(Node(graph, 'placeholder_1'))
+ self.assertTrue(len(res) == 2 and all([x.id in ['add_1', 'mul_1'] for x in res]),
+ 'get_nex_operation returned wrong op')
+
+ def test_get_next_operation_3(self):
+ # Placeholder-+--->ScaleShift
+ # +-----^
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1', 'placeholder_2_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('placeholder_2_data', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ],
+ {'mul_1_data': {'is_output': True}})
+
+ res = get_next_operation(Node(graph, 'placeholder_1'))
+ self.assertTrue(len(res) == 1 and res[0].id == 'mul_1', 'get_nex_operation returned wrong op')
diff --git a/model-optimizer/mo/middle/passes/fusing/mark_unfused_nodes_test.py b/model-optimizer/mo/middle/passes/fusing/mark_unfused_nodes_test.py
new file mode 100644
index 000000000..f68c7ed3f
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/fusing/mark_unfused_nodes_test.py
@@ -0,0 +1,312 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.middle.passes.fusing.mark_unfused_nodes import mark_unfused_nodes
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ScaleShift layer
+ 'scaleshift_1': {'type': 'ScaleShift', 'kind': 'op', 'op': 'ScaleShift'},
+ 'scaleshift_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Mul and Add operations
+ 'mul_1': {'type': 'Mul', 'kind': 'op', 'op': 'Mul'},
+ 'mul_1_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_1': {'type': 'Add', 'kind': 'op', 'op': 'Add'},
+ 'add_1_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Mul2 and Add2 operations
+ 'mul_2': {'type': 'Mul', 'kind': 'op', 'op': 'Mul'},
+ 'mul_2_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'mul_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_2': {'type': 'Add', 'kind': 'op', 'op': 'Add'},
+ 'add_2_w': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'add_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Concat1 operation
+ 'concat_1': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+ 'concat_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Convolutions
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NHWC'},
+ 'conv_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NHWC'},
+ 'conv_2_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # FullyConnected
+ 'fc_1': {'type': 'FullyConnected', 'kind': 'op', 'op': 'InnerProduct', 'layout': 'NHWC'},
+ 'fc_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Placeholders
+ 'placeholder_2': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_3': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+}
+
+
+# Unit tests for forward and backward bfs (forward_bfs, backward_bfs)
+class MarkFusedNodes(unittest.TestCase):
+ def test_mark_unfused_nodes_1(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ })
+
+ graph.graph['layout'] = 'NHWC'
+
+ mark_unfused_nodes(graph, '.*mul.*')
+
+ self.assertFalse(graph.node['mul_1']['can_be_fused'], "can_be_fused should be False")
+ self.assertFalse(graph.node['mul_2']['can_be_fused'], "can_be_fused should be False")
+ self.assertTrue(graph.node['add_1']['can_be_fused'], "can_be_fused should be True")
+
+ def test_mark_unfused_nodes_2(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ mark_unfused_nodes(graph, '.*')
+
+ self.assertFalse(graph.node['mul_1']['can_be_fused'], "can_be_fused should be False")
+ self.assertFalse(graph.node['mul_2']['can_be_fused'], "can_be_fused should be False")
+ self.assertFalse(graph.node['add_1']['can_be_fused'], "can_be_fused should be False")
+ self.assertFalse(graph.node['placeholder_1']['can_be_fused'], "can_be_fused should be False")
+ self.assertFalse(graph.node['concat_1']['can_be_fused'], "can_be_fused should be False")
+
+ def test_mark_unfused_nodes_3(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([1]), 'value': 6},
+ 'add_1_w': {'shape': np.array([1]), 'value': 6},
+ 'mul_2_w': {'shape': np.array([1]), 'value': 6},
+ 'concat_1_data': {'is_output': True}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ mark_unfused_nodes(graph, 'mul_1,add_1')
+
+ self.assertFalse(graph.node['mul_1']['can_be_fused'], "can_be_fused should be False")
+ self.assertFalse(graph.node['add_1']['can_be_fused'], "can_be_fused should be False")
+ self.assertTrue(graph.node['mul_2']['can_be_fused'], "can_be_fused should be True")
+
+ def test_mark_unfused_nodes_4(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'add_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'mul_2_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'concat_1_data': {'is_output': True}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ mark_unfused_nodes(graph, '')
+
+ self.assertTrue(graph.node['mul_1']['can_be_fused'], "can_be_fused should be True")
+ self.assertTrue(graph.node['add_1']['can_be_fused'], "can_be_fused should be True")
+ self.assertTrue(graph.node['mul_2']['can_be_fused'], "can_be_fused should be True")
+
+ def test_mark_unfused_nodes_5(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'add_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'mul_2_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'concat_1_data': {'is_output': True}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ mark_unfused_nodes(graph, '')
+
+ self.assertTrue(graph.node['mul_1']['can_be_fused'], "can_be_fused should be True")
+ self.assertTrue(graph.node['add_1']['can_be_fused'], "can_be_fused should be True")
+ self.assertTrue(graph.node['mul_2']['can_be_fused'], "can_be_fused should be True")
+
+ def test_mark_unfused_nodes_5(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'add_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'mul_2_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'concat_1_data': {'is_output': True}
+ })
+ graph.graph['layout'] = 'NCHW'
+
+ mark_unfused_nodes(graph, '')
+
+ self.assertFalse(graph.node['mul_1']['can_be_fused'], "can_be_fused should be False")
+ self.assertFalse(graph.node['add_1']['can_be_fused'], "can_be_fused should be False")
+ self.assertFalse(graph.node['mul_2']['can_be_fused'], "can_be_fused should be False")
+
+ def test_mark_unfused_nodes_6(self):
+ # Placeholder->ScaleShift->Mul->Add
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_data', 'add_1'),
+ ('add_1_w', 'add_1'),
+ ('add_1', 'add_1_data'),
+ ('add_1_data', 'mul_2'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_2_data', 'concat_1'),
+ ('concat_1', 'concat_1_data'),
+ ('placeholder_1_data', 'concat_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'add_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_2_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'add_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'mul_2_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'concat_1_data': {'is_output': True}
+ })
+ graph.graph['layout'] = 'NHWC'
+
+ mark_unfused_nodes(graph, '')
+
+ self.assertTrue(graph.node['mul_1']['can_be_fused'], "can_be_fused should be True")
+ self.assertTrue(graph.node['add_1']['can_be_fused'], "can_be_fused should be True")
+ self.assertTrue(graph.node['mul_2']['can_be_fused'], "can_be_fused should be True")
diff --git a/model-optimizer/mo/middle/passes/fusing/resnet_optimization_test.py b/model-optimizer/mo/middle/passes/fusing/resnet_optimization_test.py
new file mode 100644
index 000000000..0065775ac
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/fusing/resnet_optimization_test.py
@@ -0,0 +1,646 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.front.common.partial_infer.eltwise import eltwise_infer
+from mo.middle.passes.fusing.resnet_optimization import stride_optimization
+from mo.ops.convolution import Convolution
+from mo.ops.pooling import Pooling
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+max_elt_lambda = lambda node: eltwise_infer(node, lambda a, b: np.maximum(a, b))
+
+nodes_attributes = {
+ # Placeholders
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Concat1 operation
+ 'eltwise_1': {'type': 'Eltwise', 'kind': 'op', 'op': 'Concat', 'infer': max_elt_lambda, 'operation': 'max'},
+ 'eltwise_1_data': {'name': 'eltwise_1_data', 'value': None, 'shape': None, 'kind': 'data'},
+ # Convolutions
+ 'conv_1': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NCHW',
+ 'output_spatial_shape': None, 'output_shape': None, 'bias_term': True, 'group': 1,
+ 'spatial_dims': np.array([2, 3]),
+ 'channel_dims': np.array([1]), 'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'dilation': np.array([1, 1, 1, 1]),
+ 'batch_dims': np.array([0]), 'infer': Convolution.infer,
+ 'kernel_spatial_idx': np.array([2, 3], dtype=np.int64), 'input_feature_channel': 1,
+ 'output_feature_channel': 0, },
+ 'conv_1_w': {'value': None, 'shape': None, 'kind': 'data',
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'conv_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'conv_2': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NCHW',
+ 'output_spatial_shape': None, 'output_shape': None, 'bias_term': True, 'group': 1,
+ 'spatial_dims': np.array([2, 3]),
+ 'channel_dims': np.array([1]), 'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'dilation': np.array([1, 1, 1, 1]),
+ 'batch_dims': np.array([0]), 'infer': Convolution.infer,
+ 'kernel_spatial_idx': np.array([2, 3], dtype=np.int64), 'input_feature_channel': 1,
+ 'output_feature_channel': 0, },
+ 'conv_2_w': {'value': None, 'shape': None, 'kind': 'data',
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'conv_2_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'conv_3': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NCHW',
+ 'output_spatial_shape': None, 'output_shape': None, 'bias_term': True, 'group': 1,
+ 'spatial_dims': np.array([2, 3]),
+ 'channel_dims': np.array([1]), 'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'dilation': np.array([1, 1, 1, 1]),
+ 'batch_dims': np.array([0]), 'infer': Convolution.infer,
+ 'kernel_spatial_idx': np.array([2, 3], dtype=np.int64), 'input_feature_channel': 1,
+ 'output_feature_channel': 0, },
+ 'conv_3_w': {'value': None, 'shape': None, 'kind': 'data',
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'conv_3_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_3_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'conv_4': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NCHW',
+ 'output_spatial_shape': None, 'output_shape': None, 'bias_term': True, 'group': 1,
+ 'spatial_dims': np.array([2, 3]),
+ 'channel_dims': np.array([1]), 'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'dilation': np.array([1, 1, 1, 1]),
+ 'batch_dims': np.array([0]), 'infer': Convolution.infer,
+ 'kernel_spatial_idx': np.array([2, 3], dtype=np.int64), 'input_feature_channel': 1,
+ 'output_feature_channel': 0, },
+ 'conv_4_w': {'value': None, 'shape': None, 'kind': 'data',
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'conv_4_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_4_data': {'value': None, 'shape': None, 'kind': 'data'},
+
+ 'conv_5': {'type': 'Convolution', 'kind': 'op', 'op': 'Conv2D', 'layout': 'NCHW',
+ 'output_spatial_shape': None, 'output_shape': None, 'bias_term': True, 'group': 1,
+ 'spatial_dims': np.array([2, 3]),
+ 'channel_dims': np.array([1]), 'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'dilation': np.array([1, 1, 1, 1]),
+ 'batch_dims': np.array([0]), 'infer': Convolution.infer,
+ 'kernel_spatial_idx': np.array([2, 3], dtype=np.int64), 'input_feature_channel': 1,
+ 'output_feature_channel': 0, },
+ 'conv_5_w': {'value': None, 'shape': None, 'kind': 'data',
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'conv_5_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'conv_5_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # ReLU
+ 'relu_1': {'shape': None, 'type': 'ReLU', 'kind': 'op', 'op': 'ReLU', 'infer': copy_shape_infer},
+ 'relu_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'relu_2': {'shape': None, 'type': 'ReLU', 'kind': 'op', 'op': 'ReLU', 'infer': copy_shape_infer},
+ 'relu_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'relu_3': {'shape': None, 'type': 'ReLU', 'kind': 'op', 'op': 'ReLU', 'infer': copy_shape_infer},
+ 'relu_3_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Pooling
+ 'pool_1': {'type': 'Pooling', 'kind': 'op', 'op': 'Pooling',
+ 'spatial_dims': np.array([2, 3]),
+ 'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'infer': Pooling.infer},
+ 'pool_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+# In description of unit tests below will be used next syntax: Operation(NxM,XxY), where NxM - kernel size, XxY - stride
+class ResnetOptimizationTests(unittest.TestCase):
+ # Pl->Conv(1x1,1x1)->Conv(1x1,2x2) => Pl->Conv(1x1,2x2)->Conv(1x1,1x1)
+ def test_resnet_optimization_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_1': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3]), },
+ 'conv_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_2': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_2_data': {'shape': np.array([1, 3, 112, 112])},
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_1': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_1_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_2': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3]), },
+ 'conv_2_data': {'shape': np.array([1, 3, 112, 112])},
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+ graph_ref.graph['layout'] = 'NCHW'
+
+ stride_optimization(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_2_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Pl->Conv(3x3,2x2)->Conv(1x1,2x2) => Pl->Conv(3x3,4x4)->Conv(1x1,1x1)
+ def test_resnet_optimization_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_1': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_1_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_2': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_2_data': {'shape': np.array([1, 3, 56, 56])},
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_1': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 4, 4]),
+ 'output': np.array([3]), },
+ 'conv_1_data': {'shape': np.array([1, 3, 56, 56])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_2': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3]), },
+ 'conv_2_data': {'shape': np.array([1, 3, 56, 56])},
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+ graph_ref.graph['layout'] = 'NCHW'
+
+ stride_optimization(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_2_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Pl->Conv(3x3,2x2)->Conv(3x3,2x2) => Same
+ def test_resnet_optimization_3(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_1': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_1_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_2': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_2_data': {'shape': np.array([1, 3, 56, 56])},
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_1': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_1_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_2': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_2_data': {'shape': np.array([1, 3, 56, 56])},
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+ graph_ref.graph['layout'] = 'NCHW'
+
+ stride_optimization(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_2_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Pl--->Conv(3x3,2x2)->ReLU--->Eltwise-->Conv(1x1,2x2) => Pl--->Conv(3x3,4x4)->ReLU--->Eltwise-->Conv(1x1,1x1)
+ # `-->Conv(3x3,2x2)->ReLU---` `-->Conv(3x3,4x4)->ReLU---`
+ def test_resnet_optimization_4(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'relu_1'),
+ ('relu_1', 'relu_1_data'),
+
+ ('placeholder_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_2_data', 'relu_2'),
+ ('relu_2', 'relu_2_data'),
+
+ ('relu_1_data', 'eltwise_1'),
+ ('relu_2_data', 'eltwise_1'),
+
+ ('eltwise_1', 'eltwise_1_data'),
+ ('eltwise_1_data', 'conv_3'),
+ ('conv_3_w', 'conv_3'),
+ ('conv_3_b', 'conv_3'),
+ ('conv_3', 'conv_3_data'),
+
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_1': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_1_data': {'shape': np.array([1, 3, 112, 112])},
+ 'relu_1_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_2': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_2_data': {'shape': np.array([1, 3, 112, 112])},
+ 'relu_2_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'eltwise_1_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_3_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_3': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_3_data': {'shape': np.array([1, 3, 56, 56])},
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'relu_1'),
+ ('relu_1', 'relu_1_data'),
+
+ ('placeholder_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+ ('conv_2_data', 'relu_2'),
+ ('relu_2', 'relu_2_data'),
+
+ ('relu_1_data', 'eltwise_1'),
+ ('relu_2_data', 'eltwise_1'),
+
+ ('eltwise_1', 'eltwise_1_data'),
+ ('eltwise_1_data', 'conv_3'),
+ ('conv_3_w', 'conv_3'),
+ ('conv_3_b', 'conv_3'),
+ ('conv_3', 'conv_3_data'),
+
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_1': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 4, 4]),
+ 'output': np.array([3])},
+ 'conv_1_data': {'shape': np.array([1, 3, 56, 56])},
+ 'relu_1_data': {'shape': np.array([1, 3, 56, 56])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_2': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 4, 4]),
+ 'output': np.array([3])},
+ 'conv_2_data': {'shape': np.array([1, 3, 56, 56])},
+ 'relu_2_data': {'shape': np.array([1, 3, 56, 56])},
+
+ 'eltwise_1_data': {'shape': np.array([1, 3, 56, 56])},
+
+ 'conv_3_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_3': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3])},
+ 'conv_3_data': {'shape': np.array([1, 3, 56, 56])},
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+ graph_ref.graph['layout'] = 'NCHW'
+
+ # dump_graph_for_graphviz(graph)
+ # dump_graph_for_graphviz(graph_ref)
+
+ stride_optimization(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_3_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Pl--->Conv(1x1,1x1)->ReLU--->Eltwise-->Conv(1x1,2x2) => Pl--->Conv(1x1,2x2)->ReLU--->Eltwise-->Conv(1x1,1x1)
+ # `----------------->ReLU---` `-->Pool(1x1,2x2)->ReLU---`
+ def test_resnet_optimization_5(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'relu_1'),
+ ('relu_1', 'relu_1_data'),
+
+ ('placeholder_1_data', 'relu_2'),
+ ('relu_2', 'relu_2_data'),
+
+ ('relu_1_data', 'eltwise_1'),
+ ('relu_2_data', 'eltwise_1'),
+
+ ('eltwise_1', 'eltwise_1_data'),
+ ('eltwise_1_data', 'conv_3'),
+ ('conv_3_w', 'conv_3'),
+ ('conv_3_b', 'conv_3'),
+ ('conv_3', 'conv_3_data'),
+
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_1': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3]), },
+ 'conv_1_data': {'shape': np.array([1, 3, 224, 224])},
+ 'relu_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'relu_2_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'eltwise_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_3_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_3': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_3_data': {'shape': np.array([1, 3, 112, 112])},
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+ ('conv_1_data', 'relu_1'),
+ ('relu_1', 'relu_1_data'),
+
+ ('placeholder_1_data', 'pool_1'),
+ ('pool_1', 'pool_1_data'),
+ ('pool_1_data', 'relu_2'),
+ ('relu_2', 'relu_2_data'),
+
+ ('relu_1_data', 'eltwise_1'),
+ ('relu_2_data', 'eltwise_1'),
+
+ ('eltwise_1', 'eltwise_1_data'),
+ ('eltwise_1_data', 'conv_3'),
+ ('conv_3_w', 'conv_3'),
+ ('conv_3_b', 'conv_3'),
+ ('conv_3', 'conv_3_data'),
+
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_1': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3])},
+ 'conv_1_data': {'shape': np.array([1, 3, 112, 112])},
+ 'relu_1_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'pool_1': {'stride': np.array([1, 1, 2, 2])},
+ 'pool_1_data': {'shape': np.array([1, 3, 112, 112])},
+ 'relu_2_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'eltwise_1_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_3_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_3': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3])},
+ 'conv_3_data': {'shape': np.array([1, 3, 112, 112])},
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+ graph_ref.graph['layout'] = 'NCHW'
+
+ # dump_graph_for_graphviz(graph)
+ # dump_graph_for_graphviz(graph_ref)
+
+ stride_optimization(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_3_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ # Pl->Conv(1x1,1x1)->Conv(1x1,2x2)->Conv(3x3,1x1)->Conv(1x1,2x2)
+ # =>
+ # Pl->Conv(1x1,2x2)->Conv(1x1,1x1)->Conv(3x3,2x2)->Conv(1x1,1x1)
+ def test_resnet_optimization_6(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+
+ ('conv_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+
+ ('conv_2_data', 'conv_3'),
+ ('conv_3_w', 'conv_3'),
+ ('conv_3_b', 'conv_3'),
+ ('conv_3', 'conv_3_data'),
+
+ ('conv_3_data', 'conv_4'),
+ ('conv_4_w', 'conv_4'),
+ ('conv_4_b', 'conv_4'),
+ ('conv_4', 'conv_4_data'),
+
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_1': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3]), },
+ 'conv_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_2': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_2_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_3_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_3': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3]), },
+ 'conv_3_data': {'shape': np.array([1, 3, 110, 110])},
+
+ 'conv_4_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_4': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3]), },
+ 'conv_4_data': {'shape': np.array([1, 3, 55, 55])},
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'conv_1'),
+ ('conv_1_w', 'conv_1'),
+ ('conv_1_b', 'conv_1'),
+ ('conv_1', 'conv_1_data'),
+
+ ('conv_1_data', 'conv_2'),
+ ('conv_2_w', 'conv_2'),
+ ('conv_2_b', 'conv_2'),
+ ('conv_2', 'conv_2_data'),
+
+ ('conv_2_data', 'conv_3'),
+ ('conv_3_w', 'conv_3'),
+ ('conv_3_b', 'conv_3'),
+ ('conv_3', 'conv_3_data'),
+
+ ('conv_3_data', 'conv_4'),
+ ('conv_4_w', 'conv_4'),
+ ('conv_4_b', 'conv_4'),
+ ('conv_4', 'conv_4_data'),
+
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 3, 224, 224])},
+
+ 'conv_1_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_1': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3])},
+ 'conv_1_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_2_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_2': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3])},
+ 'conv_2_data': {'shape': np.array([1, 3, 112, 112])},
+
+ 'conv_3_w': {'value': np.zeros([3, 3, 3, 3]), 'shape': np.array([3, 3, 3, 3])},
+ 'conv_3': {'kernel_spatial': np.array([3, 3]),
+ 'stride': np.array([1, 1, 2, 2]),
+ 'output': np.array([3])},
+ 'conv_3_data': {'shape': np.array([1, 3, 55, 55])},
+
+ 'conv_4_w': {'value': np.zeros([3, 3, 1, 1]), 'shape': np.array([3, 3, 1, 1])},
+ 'conv_4': {'kernel_spatial': np.array([1, 1]),
+ 'stride': np.array([1, 1, 1, 1]),
+ 'output': np.array([3])},
+ 'conv_4_data': {'shape': np.array([1, 3, 55, 55])},
+ },
+ nodes_with_edges_only=True)
+
+ graph.graph['layout'] = 'NCHW'
+ graph_ref.graph['layout'] = 'NCHW'
+
+ stride_optimization(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'conv_4_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/mo/middle/passes/infer_test.py b/model-optimizer/mo/middle/passes/infer_test.py
new file mode 100644
index 000000000..d3b7e6597
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/infer_test.py
@@ -0,0 +1,531 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.concat import concat_infer
+from mo.graph.graph import Node
+from mo.middle.passes.infer import override_placeholder_shapes, partial_infer, add_mean_scale_values, scale_input, \
+ check_for_cycle
+from mo.utils.cli_parser import get_mean_scale_dictionary, parse_tuple_pairs
+from mo.utils.error import Error
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_1_data': {'value': None, 'kind': 'data', 'data_type': None},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'concat': {'type': 'Concat', 'value': None, 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_3_data': {'value': None, 'kind': 'data', 'data_type': None},
+ # Placeholders
+ 'placeholder_1': {'shape': None, 'type': 'Input', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_2': {'shape': None, 'type': 'Input', 'kind': 'op', 'op': 'Placeholder'},
+ 'pl_1': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'pl_1_data': {'value': None, 'kind': 'data', 'data_type': None},
+ 'pl_2': {'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'pl_2_data': {'value': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ScaleShift layer
+ 'scaleshift_1': {'type': 'ScaleShift', 'kind': 'op', 'op': 'ScaleShift'},
+ 'scaleshift_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Mul op
+ 'mul_1': {'type': None, 'kind': 'op', 'op': 'Mul'},
+ 'mul_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mul_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ }
+
+
+class TestInferPass(unittest.TestCase):
+ def test_override_placeholder_shapes(self):
+ """
+ Test for overriding shape in placeholder by shape from user_shapes.
+ """
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227]), 'op': 'Placeholder'}
+ },
+ nodes_with_edges_only=True)
+
+ ph_shape = np.array([1, 3, 224, 224])
+ user_dict = {'node_1': [{'shape': ph_shape}]}
+ override_placeholder_shapes(graph, user_dict)
+ res_shape = graph.node['node_1']['shape']
+ self.assertTrue(np.array_equal(ph_shape, res_shape))
+
+ def test_override_placeholder_no_shape(self):
+ """
+ Test for case when user_shapes is not defined.
+ """
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None, 'op': 'Placeholder'},
+ 'node_1': {'shape': np.array([1, 3, 227, 227]), 'op': 'Placeholder'}
+ },
+ nodes_with_edges_only=True)
+ out = override_placeholder_shapes(graph, None)
+ res_shape = graph.node['node_1']['shape']
+ placeholder_shape = np.array([1, 3, 227, 227])
+ self.assertIsNone(out)
+ self.assertTrue(np.array_equal(placeholder_shape, res_shape))
+
+ def test_override_placeholder_shapes(self):
+ """
+ Test for case when user_shapes is not None, but it shouldn't rewrite shapes.
+ """
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227]), 'op': 'Placeholder'}
+ },
+ nodes_with_edges_only=True)
+
+ node_1_shape = np.array([1, 3, 227, 227])
+ user_dict = {'some_node': [{'shape': np.zeros((3))}]}
+ override_placeholder_shapes(graph, user_dict)
+ res_shape = graph.node['node_1']['shape']
+ self.assertTrue(np.array_equal(node_1_shape, res_shape))
+
+ def test_override_placeholder_shapes_dict(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None, 'op': 'Placeholder'},
+ 'node_1': {'shape': np.array([1, 3, 227, 227]), 'op': 'Placeholder'}
+ },
+ nodes_with_edges_only=True)
+
+ placeholder_shape = np.array([1, 3, 224, 224])
+ user_shapes = {
+ 'node_1': [{'shape': placeholder_shape}],
+ 'node_2': [{'shape': placeholder_shape}],
+ }
+ override_placeholder_shapes(graph, user_shapes)
+ res_shape = graph.node['node_1']['shape']
+ res_shape2 = graph.node['node_2']['shape']
+ self.assertTrue(np.array_equal(placeholder_shape, res_shape))
+ self.assertTrue(np.array_equal(placeholder_shape, res_shape2))
+
+ nodes = {
+ 'placeholder_1': {'name': 'placeholder_1', 'shape': [1, 2, 3, 4], 'type': 'Placeholder', 'value': None,
+ 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2': {'name': 'placeholder_2', 'shape': [5, 6, 7, 8], 'type': 'Placeholder', 'value': None,
+ 'kind': 'op', 'op': 'Placeholder'},
+ '1': {'name': 'node_1', 'type': 'Identity', 'value': None, 'kind': 'op'},
+ '2': {'name': 'node_2', 'type': 'Identity', 'value': None, 'kind': 'op'},
+ '3': {'name': 'concat', 'type': 'Identity', 'value': None, 'kind': 'op'},
+ '4': {'name': 'output', 'type': 'SoftMax', 'value': None, 'kind': 'op'}
+ }
+ edges = [
+ ('placeholder_1', '1'),
+ ('1', '3'),
+ ('placeholder_2', '2'),
+ ('2', '3'),
+ ('3', '4')
+ ]
+
+ def test_override_placeholder_shapes_batch_is_not_set(self):
+ """
+ Test case when batch is not set. (shapes shouldn't change)
+ """
+ graph = build_graph(self.nodes, self.edges)
+ shapes = {}
+ batch = None
+ override_placeholder_shapes(graph, shapes, batch)
+ res_shape_1 = graph.node['placeholder_1']['shape']
+ res_shape_2 = graph.node['placeholder_2']['shape']
+ self.assertTrue(np.array_equal(self.nodes['placeholder_1']['shape'], res_shape_1))
+ self.assertTrue(np.array_equal(self.nodes['placeholder_2']['shape'], res_shape_2))
+
+ def test_override_placeholder_shapes_real_inputs_and_batch(self):
+ """
+ Test case when batch is set and shapes should overwrite by user shapes.
+ """
+ graph = build_graph(self.nodes, self.edges)
+ shapes = {'placeholder_1': [{'shape': np.array([1, 2, 3, 4])}],
+ 'placeholder_2': [{'shape': np.array([1, 5, 6, 7])}]}
+ batch = 4
+ override_placeholder_shapes(graph, shapes, batch)
+ res_shape_1 = graph.node['placeholder_1']['shape']
+ res_shape_2 = graph.node['placeholder_2']['shape']
+ self.assertTrue(np.array_equal(res_shape_1, np.array([4, 2, 3, 4])))
+ self.assertTrue(np.array_equal(res_shape_2, np.array([4, 5, 6, 7])))
+
+ def test_override_placeholder_shapes_real_inputs_and_batch_2(self):
+ """
+ Test case when batch is set, but shapes in user_shapes is None.
+ """
+ graph = build_graph(self.nodes, self.edges)
+ shapes = {'placeholder_1': [{'shape': None}], 'placeholder_2': [{'shape': None}]}
+ batch = 4
+ graph.node['placeholder_2']['shape'] = np.array([1, 2, 3, 4])
+ graph.node['placeholder_2']['shape'] = np.array([1, 5, 6, 7])
+ override_placeholder_shapes(graph, shapes, batch)
+ np.testing.assert_array_equal(graph.node['placeholder_1']['shape'], np.array([4, 2, 3, 4]))
+ np.testing.assert_array_equal(graph.node['placeholder_2']['shape'], np.array([4, 5, 6, 7]))
+
+ def test_partial_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'concat'),
+ ('node_2', 'concat'),
+ ('concat', 'node_3')],
+ {'node_3': {'kind': 'data', 'is_output': True, 'shape': None, 'infer': None},
+ 'node_1': {'kind': 'data', 'shape': np.array([1, 3, 227, 227]), 'infer': None},
+ 'node_2': {'kind': 'data', 'shape': np.array([1, 3, 227, 227]), 'infer': None},
+ 'concat': {'kind': 'op', 'axis': 2, 'infer': concat_infer}
+ },
+ nodes_with_edges_only=True)
+
+ start_node = 'concat'
+ partial_infer(graph, start_node)
+ node = Node(graph, start_node)
+ self.assertTrue(node.is_partial_inferred)
+ self.assertTrue(node.out_node().is_partial_inferred)
+
+ # check if previous nodes are not inferred
+ node = Node(graph, start_node)
+ while True:
+ # collect nodes in a list
+ if isinstance(node.in_nodes(), list):
+ in_nodes = node.in_nodes()
+ else:
+ in_nodes = [y for x, y in node.in_nodes().items()]
+
+ # check parents and find next parent
+ for n in in_nodes:
+ if 'embedded_input_' not in n.id:
+ node = n
+ self.assertFalse(n.has('is_partial_inferred'))
+
+ if not len(in_nodes):
+ break
+
+ def test_partial_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None, 'infer': None},
+ 'node_1': {'shape': None, 'infer': None}
+ },
+ nodes_with_edges_only=True)
+ self.assertRaises(Error, partial_infer, graph, 'node_1')
+
+ def test_partial_infer_cycle(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'concat'),
+ ('node_2', 'concat'),
+ ('concat', 'node_3'),
+ ('node_3', 'concat')],
+ {'node_3': {'kind': 'data', 'is_output': True, 'shape': None, 'infer': None},
+ 'node_1': {'kind': 'data', 'shape': np.array([1, 3, 227, 227]), 'infer': None},
+ 'node_2': {'kind': 'data', 'shape': np.array([1, 3, 227, 227]), 'infer': None},
+ 'concat': {'kind': 'op', 'axis': 2, 'infer': concat_infer}
+ },
+ nodes_with_edges_only=True)
+
+ start_node = 'concat'
+ self.assertRaises(Error, partial_infer, graph, start_node)
+
+ def test_add_mean_scale_values_with_data_name(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None, 'data_type': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227]), 'op': 'Placeholder', 'name': 'data',
+ 'data_type': None}
+ },
+ nodes_with_edges_only=True)
+ graph.graph['layout'] = 'NCHW'
+ mean_values = parse_tuple_pairs('(124,117,104)')
+ scale_values = parse_tuple_pairs('')
+
+ # input = 'data'
+ mean_scale = get_mean_scale_dictionary(mean_values, scale_values, None)
+ self.assertEqual(len(graph), 2)
+ add_mean_scale_values(graph, mean_scale)
+ self.assertEqual(len(graph), 5)
+
+ def test_add_mean_scale_values_without_data_name(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None, 'data_type': None},
+ 'node_1': {'shape': np.array([1, 3, 227, 227]), 'op': 'Placeholder', 'name': 'data',
+ 'data_type': None}
+ },
+ nodes_with_edges_only=True)
+ graph.graph['layout'] = 'NCHW'
+ mean_values = parse_tuple_pairs('(124,117,104)')
+ scale_values = parse_tuple_pairs('')
+ # input = None
+ mean_scale = get_mean_scale_dictionary(mean_values, scale_values, None)
+ self.assertEqual(len(graph), 2)
+ add_mean_scale_values(graph, mean_scale)
+ self.assertEqual(len(graph), 5)
+
+ def test_add_mean_scale_values1(self):
+ graph = build_graph(nodes_attributes,
+ [('pl_1', 'pl_1_data'), ('pl_2', 'pl_2_data')],
+ {'pl_1_data': {'shape': np.array([1, 3, 38, 38]), 'infer': None},
+ 'pl_2_data': {'shape': np.array([1, 6]), 'infer': None},
+ 'pl_1': {'shape': np.array([1,3,38,38])},
+ 'pl_2': {'shape': np.array([1,6])},
+ },
+ nodes_with_edges_only=True)
+ graph.graph['layout'] = 'NCHW'
+ add_mean_scale_values(graph,
+ {'pl_1': {'mean': np.array([1., 2., 3.])}, 'pl_2': {'mean': np.array([0., 0., 0.])}})
+ mul_op_cnt = 0
+ add_op_cnt = 0
+ for node in graph.nodes():
+ node = Node(graph, node)
+ if node.has_valid('op') and node.op == 'Mul':
+ mul_op_cnt += 1
+ if node.has_valid('op') and node.op == 'Add':
+ add_op_cnt += 1
+
+ self.assertEqual(add_op_cnt, 1, "Found more than one Add op in graph")
+ self.assertEqual(mul_op_cnt, 0, "Found Mul op in graph")
+
+ def test_optimize_scale_and_add_mean_values(self):
+ graph = build_graph(
+ nodes_attributes,
+ [
+ ('pl_1', 'pl_1_data')
+ ],
+ {
+ 'pl_1_data': {
+ 'shape': np.array([1, 3, 38, 38]),
+ 'infer': None
+ },
+ 'pl_1': {
+ 'shape': np.array([1,3,38,38])
+ }
+ },
+ nodes_with_edges_only=True
+ )
+ graph.graph['layout'] = 'NCHW'
+ add_mean_scale_values(graph,
+ {
+ 'pl_1': {
+ 'scale': np.array([1.]),
+ 'mean': np.array([1., 2., 3.])
+ }
+ })
+ mul_op_cnt = 0
+ add_op_cnt = 0
+ for node in graph.nodes():
+ node = Node(graph, node)
+ if node.has_valid('op') and node.op == 'Mul':
+ mul_op_cnt += 1
+ if node.has_valid('op') and node.op == 'Add':
+ add_op_cnt += 1
+
+ self.assertEqual(add_op_cnt, 1, "Found more than one Add op in graph")
+ self.assertEqual(mul_op_cnt, 0, "Found Mul op in graph")
+
+ def test_optimize_mean_and_add_scale_values(self):
+ graph = build_graph(
+ nodes_attributes,
+ [
+ ('pl_1', 'pl_1_data')
+ ],
+ {
+ 'pl_1_data': {
+ 'shape': np.array([1, 3, 38, 38]),
+ 'infer': None
+ },
+ 'pl_1': {
+ 'shape': np.array([1,3,38,38])
+ }
+ },
+ nodes_with_edges_only=True
+ )
+ graph.graph['layout'] = 'NCHW'
+ add_mean_scale_values(graph,
+ {
+ 'pl_1': {
+ 'scale': np.array([1.43]),
+ 'mean': np.array([0., 0., 0.])
+ }
+ })
+ mul_op_cnt = 0
+ add_op_cnt = 0
+ for node in graph.nodes():
+ node = Node(graph, node)
+ if node.has_valid('op') and node.op == 'Mul':
+ mul_op_cnt += 1
+ if node.has_valid('op') and node.op == 'Add':
+ add_op_cnt += 1
+
+ self.assertEqual(add_op_cnt, 0, "Found more than one Add op in graph")
+ self.assertEqual(mul_op_cnt, 1, "Found Mul op in graph")
+
+ def test_add_mean_scale_values3(self):
+ graph = build_graph(nodes_attributes,
+ [('pl_1', 'pl_1_data')],
+ {'pl_1_data': {'shape': np.array([1, 3, 38, 38]), 'infer': None},
+ 'pl_1': {'shape': np.array([1,3,38,38])},
+ },
+ nodes_with_edges_only=True)
+ graph.graph['layout'] = 'NCHW'
+ add_mean_scale_values(graph, [[np.array([1., 2., 3.]), np.array([1., 2., 3.])]])
+
+ mul_op_cnt = 0
+ add_op_cnt = 0
+ for node in graph.nodes():
+ node = Node(graph, node)
+ if node.has_valid('op') and node.op == 'Mul':
+ mul_op_cnt += 1
+ if node.has_valid('op') and node.op == 'Add':
+ add_op_cnt += 1
+
+ self.assertEqual(add_op_cnt, 1, "Found more than one Add op in graph")
+ self.assertEqual(mul_op_cnt, 1, "Found more than one Nul op in graph")
+
+ def test_add_mean_scale_values_cut_graph(self):
+ """
+ Test case when user cutted start of the network and specified mean/scale value to the new input node 'node_3'.
+ """
+ graph = build_graph(nodes_attributes,
+ [('pl_1', 'pl_1_data'),
+ ('pl_2', 'pl_2_data'),
+ ('pl_2_data', 'node_3'),
+ ('node_3', 'node_3_data'),
+ ('pl_1_data', 'node_1'),
+ ('node_3_data', 'node_1'),
+ ],
+ {'pl_1_data': {'shape': np.array([1, 3, 38, 38]), 'infer': None},
+ 'pl_2_data': {'shape': np.array([1, 3, 38, 38]), 'infer': None},
+ 'pl_2': {'initial_node_name': 'node_3', 'shape': np.array([1,3,38,38])},
+ 'pl_1': {'shape': np.array([1,3,38,38])},
+ },
+ nodes_with_edges_only=True)
+ graph.graph['layout'] = 'NCHW'
+ add_mean_scale_values(graph, {'pl_1': {'mean': np.array([1, 2, 3])}, 'node_3': {'scale': np.array([1, 2, 3])}})
+
+ mul_op_cnt = 0
+ add_op_cnt = 0
+ for node in graph.nodes():
+ node = Node(graph, node)
+ if node.has_valid('op') and node.op == 'Mul':
+ mul_op_cnt += 1
+ if node.has_valid('op') and node.op == 'Add':
+ add_op_cnt += 1
+
+ self.assertEqual(add_op_cnt, 1, "There should be exactly one Add op")
+ self.assertEqual(mul_op_cnt, 1, "There should be exactly one Mul op")
+ self.assertEqual(Node(graph, 'pl_2').out_node().out_node().op, 'Mul', "The Mul op should be added after pl_2")
+ self.assertEqual(Node(graph, 'pl_1').out_node().out_node().op, 'Add', "The Add op should be added after pl_1")
+
+
+class ScaleInputTests(unittest.TestCase):
+ def test_scale_input_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data')],
+ {'placeholder_1_data': {'is_output': True},
+ 'placeholder_1': {'shape': np.array([1, 3, 224, 224])}
+ },
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'mul_1_data'),
+ ('mul_1_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'placeholder_1_data')],
+ {'mul_1_w': {'shape': np.array([1, 1, 1]), 'value': np.array([1 / 255])},
+ 'placeholder_1_data': {'is_output': True}},
+ nodes_with_edges_only=True)
+ graph.graph['layout'] = 'NCHW'
+ scale_input(graph, 255)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'placeholder_1_data')
+ self.assertTrue(flag, resp)
+
+ def test_scale_input_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data')],
+ {'placeholder_1_data': {'is_output': True}},
+ nodes_with_edges_only=True)
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data')],
+ {'placeholder_1_data': {'is_output': True}},
+ nodes_with_edges_only=True)
+
+ scale_input(graph, 1)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'placeholder_1_data')
+ self.assertTrue(flag, resp)
+
+ def test_check_for_cycle1(self):
+ # cyclic case
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_1_data'),
+ ('node_1_data', 'node_3'),
+ ('node_3', 'node_3_data'),
+ ('node_3_data', 'node_1')],
+ nodes_with_edges_only=True)
+ with self.assertRaisesRegex(Error, 'Graph contains a cycle. Can not proceed.*'):
+ check_for_cycle(graph)
+
+ def test_check_for_cycle2(self):
+ # acyclic case
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_1_data'),
+ ('node_1_data', 'node_3'),
+ ('node_3', 'node_3_data'),
+ ('node_3_data', 'mul_1'),
+ ('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data')
+ ],
+ nodes_with_edges_only=True)
+ try:
+ check_for_cycle(graph)
+ except Error:
+ self.fail("Unexpected Error raised")
+
+ def test_is_not_fully_inferred_param(self):
+ # Node that have is_not_fully_inferred=True
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'concat'),
+ ('node_2', 'concat'),
+ ('concat', 'node_3')],
+ {'node_3': {'kind': 'data', 'is_output': True, 'shape': None, 'infer': None},
+ 'node_1': {'kind': 'data', 'shape': np.array([1, 3, 227, 227]), 'infer': None},
+ 'node_2': {'kind': 'data', 'shape': np.array([1, 3, 227, 227]), 'infer': None},
+ 'concat': {'kind': 'op', 'axis': 2, 'infer': concat_infer, 'is_not_fully_inferred': True}
+ },
+ nodes_with_edges_only=True)
+
+ start_node = 'concat'
+ try:
+ partial_infer(graph, start_node)
+ except Error:
+ self.fail("Unexpected Error raised")
+ node = Node(graph, start_node)
+ self.assertTrue(node.is_partial_inferred)
+ self.assertTrue(node.out_node().is_partial_inferred)
+
+ def test_for_is_cyclic1(self):
+ # Test for case of cyclic graph without is_cyclic attrs
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'node_1_data'),
+ ('node_1_data', 'node_3'),
+ ('node_3', 'node_3_data'),
+ ('node_3_data', 'node_1')],
+ nodes_with_edges_only=True)
+ with self.assertRaisesRegex(Error, 'Graph contains a cycle. Can not proceed.*'):
+ partial_infer(graph)
diff --git a/model-optimizer/mo/middle/passes/mean_scale_values_test.py b/model-optimizer/mo/middle/passes/mean_scale_values_test.py
new file mode 100644
index 000000000..9bc7b6bd6
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/mean_scale_values_test.py
@@ -0,0 +1,157 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.middle.passes.mean_scale_values import move_scaleshift_to_preprocess
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'concat': {'type': 'Concat', 'value': None, 'kind': 'op'},
+ 'node_3': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ # Placeholders
+ 'placeholder_1': {'value': None, 'shape': None, 'type': 'Input', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ 'placeholder_2': {'value': None, 'shape': None, 'type': 'Input', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_2_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # ScaleShift layer
+ 'scaleshift_1': {'type': 'ScaleShift', 'value': None, 'kind': 'op', 'op': 'ScaleShift'},
+ 'scaleshift_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_b': {'value': None, 'shape': None, 'kind': 'data'},
+ 'scaleshift_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ }
+
+
+class TestScaleShift_To_Preprocess(unittest.TestCase):
+ def test_move_scaleshift_to_preprocess_1(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.ones(3)},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([-1, -2, -3])},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ del graph['placeholder_1']['placeholder_1_data'][0]['in']
+ del graph['scaleshift_1']['scaleshift_1_data'][0]['in']
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'scaleshift_1_data')],
+ {'scaleshift_1_data': {'is_output': True}})
+
+ move_scaleshift_to_preprocess(graph)
+ self.assertTrue(graph.graph['mean_values'] is not None)
+ self.assertTrue(np.array_equal(graph.graph['mean_values']['placeholder_1'], np.array([1, 2, 3])))
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+ def test_move_scaleshift_to_preprocess_2(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array((1, 2, 3))},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([-1, -2, -3])},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ del graph['placeholder_1']['placeholder_1_data'][0]['in']
+ del graph['scaleshift_1']['scaleshift_1_data'][0]['in']
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3]), 'is_output': True},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array((1, 2, 3))},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.array([-1, -2, -3])},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ move_scaleshift_to_preprocess(graph)
+ self.assertTrue(graph.graph.get('mean_values', None) is None)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+ def test_move_scaleshift_to_preprocess_3(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_w', 'scaleshift_1'), ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array((1, 2, 3))},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ del graph['placeholder_1']['placeholder_1_data'][0]['in']
+ del graph['scaleshift_1']['scaleshift_1_data'][0]['in']
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_w', 'scaleshift_1')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3]), 'is_output': True},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.array((1, 2, 3))},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ move_scaleshift_to_preprocess(graph)
+ self.assertTrue(graph.graph.get('mean_values', None) == None)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
+
+ def test_move_scaleshift_to_preprocess_4(self):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'scaleshift_1'),
+ ('scaleshift_1', 'scaleshift_1_data'),
+ ('scaleshift_1_w', 'scaleshift_1'),
+ ('scaleshift_1_b', 'scaleshift_1')],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'scaleshift_1_w': {'shape': np.array([3]), 'value': np.ones(3)},
+ 'scaleshift_1_b': {'shape': np.array([3]), 'value': np.zeros(3)},
+ 'scaleshift_1_data': {'is_output': True}
+ })
+
+ del graph['placeholder_1']['placeholder_1_data'][0]['in']
+ del graph['scaleshift_1']['scaleshift_1_data'][0]['in']
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'scaleshift_1_data')],
+ {'scaleshift_1_data': {'is_output': True}})
+
+ move_scaleshift_to_preprocess(graph)
+ self.assertTrue(graph.graph.get('mean_values', None) is None)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'scaleshift_1_data')
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/mo/middle/passes/pool_test.py b/model-optimizer/mo/middle/passes/pool_test.py
new file mode 100644
index 000000000..1473f1eeb
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/pool_test.py
@@ -0,0 +1,96 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.middle.passes.eliminate import graph_clean_up
+from mo.middle.passes.pool import mean_to_avgpool
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ 'placeholder_1': {'shape': None, 'type': 'Placeholder', 'kind': 'op', 'op': 'Placeholder'},
+ 'placeholder_1_data': {'value': None, 'shape': None, 'kind': 'data', 'data_type': None},
+ # Mean layer
+ 'mean_1': {'type': 'Pooling', 'kind': 'op', 'op': 'Mean', 'keep_dims': True},
+ 'mean_axis': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mean_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # AvgPool layer
+ 'pool_1': {'type': 'Pooling', 'kind': 'op', 'op': 'Power', 'scale': None, 'shift': None, 'power': None},
+ 'pool_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Reshape layer
+ 'reshape_1': {'type': 'Reshape', 'kind': 'op', 'op': 'Reshape'},
+ 'reshape_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class MeanToAvgPoolTests(unittest.TestCase):
+ def _create_graph_with_mean(self, axis, keep_dims=True, mean_out_shape=np.array([1, 227, 227, 3])):
+ graph = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'mean_1'),
+ ('mean_1', 'mean_1_data'),
+ ('mean_axis', 'mean_1'),
+ ],
+ {'placeholder_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'mean_1': {'shape': np.array([1, 227, 227, 3]), 'keep_dims': keep_dims},
+ 'mean_axis': {'shape': np.array(axis.shape) if axis is not None else None,
+ 'value': np.array(axis) if axis is not None else None},
+ 'mean_1_data': {'shape': mean_out_shape, 'is_output': True},
+ })
+ del graph['mean_1']['mean_1_data'][0]['in']
+ return graph
+
+ def test_mean_to_avg_1(self):
+ graph = self._create_graph_with_mean(axis=np.array([1, 2]))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'pool_1'),
+ ('pool_1', 'pool_1_data'),
+ ],
+ {'pool_1': {'pool_method': 'avg', 'rounding_type': 'ceil', 'exclude_pad': 'true',
+ 'op': 'AvgPool', 'shape': np.array([1, 227, 227, 3])},
+ 'pool_1_data': {'is_output': True, 'shape': np.array([1, 227, 227, 3])}
+ })
+
+ mean_to_avgpool(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'mean_1_data', 'pool_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
+
+ def test_mean_to_avg_2(self):
+ graph = self._create_graph_with_mean(axis=np.array([0]), keep_dims=False,
+ mean_out_shape=np.array([227, 227, 3]))
+
+ graph_ref = build_graph(nodes_attributes,
+ [('placeholder_1', 'placeholder_1_data'),
+ ('placeholder_1_data', 'pool_1'),
+ ('pool_1', 'pool_1_data'),
+ ('pool_1_data', 'reshape_1'),
+ ('reshape_1', 'reshape_1_data')
+ ],
+ {'pool_1': {'pool_method': 'avg', 'rounding_type': 'ceil', 'exclude_pad': 'true',
+ 'op': 'AvgPool', 'shape': np.array([1, 227, 227, 3])},
+ 'pool_1_data': {'shape': np.array([1, 227, 227, 3])},
+ 'reshape_1_data': {'is_output': True, 'shape': np.array([227, 227, 3])},
+ })
+
+ mean_to_avgpool(graph)
+ graph_clean_up(graph)
+ (flag, resp) = compare_graphs(graph, graph_ref, 'mean_1_data', 'reshape_1_data', check_op_attrs=True)
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/mo/middle/passes/shared_weights_duplication_test.py b/model-optimizer/mo/middle/passes/shared_weights_duplication_test.py
new file mode 100644
index 000000000..ef482762f
--- /dev/null
+++ b/model-optimizer/mo/middle/passes/shared_weights_duplication_test.py
@@ -0,0 +1,77 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.middle.passes.shared_weights_duplication import duplicate_shared_weights
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+nodes_attributes = {
+ # Mul and Add operations
+ 'mul_1': {'type': None, 'kind': 'op', 'op': 'Mul'},
+ 'mul_1_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mul_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mul_2': {'type': None, 'kind': 'op', 'op': 'Mul'},
+ 'mul_2_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mul_2_data': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mul_3': {'type': None, 'kind': 'op', 'op': 'Mul'},
+ 'mul_3_w': {'value': None, 'shape': None, 'kind': 'data'},
+ 'mul_3_data': {'value': None, 'shape': None, 'kind': 'data'},
+ # Concat1 operation
+ 'concat_1': {'type': 'Concat', 'kind': 'op', 'op': 'Concat'},
+ 'concat_1_data': {'value': None, 'shape': None, 'kind': 'data'},
+}
+
+
+class DuplicateSharedWeightsTests(unittest.TestCase):
+ def test_duplicate_shared_weights_1(self):
+ graph = build_graph(nodes_attributes,
+ [('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_1_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_1_w', 'mul_3'),
+ ('mul_3', 'mul_3_data'),
+ ('mul_1_data', 'concat_1'),
+ ('mul_2_data', 'concat_1'),
+ ('mul_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])}})
+
+ graph_ref = build_graph(nodes_attributes,
+ [('mul_1_w', 'mul_1'),
+ ('mul_1', 'mul_1_data'),
+ ('mul_2_w', 'mul_2'),
+ ('mul_2', 'mul_2_data'),
+ ('mul_3_w', 'mul_3'),
+ ('mul_3', 'mul_3_data'),
+ ('mul_1_data', 'concat_1'),
+ ('mul_2_data', 'concat_1'),
+ ('mul_3_data', 'concat_1'),
+ ('concat_1', 'concat_1_data')
+ ],
+ {'mul_1_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'mul_2_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ 'mul_3_w': {'shape': np.array([3]), 'value': np.array([1, 2, 3])},
+ })
+
+ duplicate_shared_weights(graph)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'concat_1_data')
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/mo/ops/activation_test.py b/model-optimizer/mo/ops/activation_test.py
new file mode 100644
index 000000000..b289b96b4
--- /dev/null
+++ b/model-optimizer/mo/ops/activation_test.py
@@ -0,0 +1,111 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.ops.activation import Activation
+from mo.utils.unittest.graph import build_graph
+
+
+class TestActivationOp(unittest.TestCase):
+ nodes_attributes = {
+ 'node_1': {
+ 'shape': np.array([227, 227, 227, 227]),
+ 'value': None
+ },
+ 'activation_node': {
+ 'op': 'Activation',
+ 'kind': 'op'
+ },
+ 'node_3': {
+ 'shape': None
+ }
+ }
+
+ def test_assertion_activation_infer(self):
+ graph = build_graph(self.nodes_attributes,
+ [
+ ('node_1', 'activation_node'),
+ ('activation_node', 'node_3')
+ ],
+ {
+ 'activation_node': {'operation': 'test'}
+ })
+ activation_node = Node(graph, 'activation_node')
+ self.assertEqual(activation_node.op, 'Activation')
+ self.assertRaises(KeyError, Activation.infer, activation_node)
+
+ def test_activation_infer(self):
+ graph = build_graph(self.nodes_attributes,
+ [
+ ('node_1', 'activation_node'),
+ ('activation_node', 'node_3')
+ ],
+ {
+ 'node_1': {
+ 'value': np.array([0, 7, 3, -1])
+ },
+ 'activation_node': {
+ 'operation': 'relu6'
+ },
+ 'node_3': {
+ 'value': None
+ }
+ })
+ graph.graph['layout'] = 'NCHW'
+ activation_node = Node(graph, 'activation_node')
+ Activation.infer(activation_node)
+ exp_shape = np.array([227, 227, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ res_value = graph.node['node_3']['value']
+ exp_value = np.array([0, 6, 3, 0])
+ for i, value in enumerate(exp_shape):
+ self.assertEqual(res_shape[i], value)
+ for i, value in enumerate(exp_value):
+ self.assertEqual(res_value[i], value)
+
+ def test_activation_elu_infer(self):
+ graph = build_graph(self.nodes_attributes,
+ [
+ ('node_1', 'activation_node'),
+ ('activation_node', 'node_3')
+ ],
+ {
+ 'node_1': {
+ 'value': np.array([6, -4, -2, -1])
+ },
+ 'activation_node': {
+ 'operation': 'elu',
+ 'alpha': 1.0,
+ },
+ 'node_3': {
+ 'value': None
+ }
+ })
+ graph.graph['layout'] = 'NCHW'
+ activation_node = Node(graph, 'activation_node')
+ Activation.infer(activation_node)
+ exp_shape = np.array([227, 227, 227, 227])
+ res_shape = graph.node['node_3']['shape']
+ res_value = graph.node['node_3']['value']
+ exp_value = np.array([6., -0.98168436, -0.86466472, -0.63212056])
+ for i, value in enumerate(exp_shape):
+ self.assertEqual(res_shape[i], value)
+ for i, value in enumerate(exp_value):
+ self.assertAlmostEqual(res_value[i], value)
diff --git a/model-optimizer/mo/ops/clamp_test.py b/model-optimizer/mo/ops/clamp_test.py
new file mode 100644
index 000000000..66e38e22b
--- /dev/null
+++ b/model-optimizer/mo/ops/clamp_test.py
@@ -0,0 +1,47 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.elemental import copy_shape_infer
+from mo.ops.clamp import Clamp
+from mo.utils.unittest.graph import build_graph
+
+
+class TestClampOp(unittest.TestCase):
+ nodes_attributes = {
+ 'node_1': {
+ 'shape': np.array([227, 227, 227, 227])
+ },
+ 'clamp_node': {
+ },
+ 'node_3': {
+ 'kind': 'data'
+ }
+ }
+
+ def test_clamp_op(self):
+ graph = build_graph(self.nodes_attributes,
+ [
+ ('node_1', 'clamp_node'),
+ ('clamp_node', 'node_3')
+ ])
+ clamp_node = Clamp(graph, self.nodes_attributes['clamp_node']).add_node()
+ self.assertEqual(clamp_node.type, 'Clamp')
+ self.assertEqual(clamp_node.op, 'Clamp')
+ self.assertEqual(clamp_node.infer, copy_shape_infer)
diff --git a/model-optimizer/mo/ops/concat_test.py b/model-optimizer/mo/ops/concat_test.py
new file mode 100644
index 000000000..7f39236df
--- /dev/null
+++ b/model-optimizer/mo/ops/concat_test.py
@@ -0,0 +1,47 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.concat import concat_infer
+from mo.ops.concat import Concat
+from mo.utils.unittest.graph import build_graph
+
+
+class TestConcatOp(unittest.TestCase):
+ nodes_attributes = {
+ 'node_1': {
+ 'shape': np.array([227, 227, 227, 227])
+ },
+ 'concat_node': {
+ },
+ 'node_3': {
+ 'kind': 'data'
+ }
+ }
+
+ def test_concat_op(self):
+ graph = build_graph(self.nodes_attributes,
+ [
+ ('node_1', 'concat_node'),
+ ('concat_node', 'node_3')
+ ])
+ concat_node = Concat(graph, self.nodes_attributes['concat_node']).add_node()
+ self.assertEqual(concat_node.type, 'Concat')
+ self.assertEqual(concat_node.op, 'Concat')
+ self.assertEqual(concat_node.infer, concat_infer)
diff --git a/model-optimizer/mo/ops/convolution_test.py b/model-optimizer/mo/ops/convolution_test.py
new file mode 100644
index 000000000..6f009b588
--- /dev/null
+++ b/model-optimizer/mo/ops/convolution_test.py
@@ -0,0 +1,371 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.utils import int64_array
+from mo.graph.graph import Node
+from mo.ops.convolution import Convolution
+from mo.utils.unittest.extractors import FakeValue
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'conv_input': {'value': None, 'kind': 'data'},
+ 'conv_node': {'type': 'Convolution', 'kind': 'op'},
+ 'conv_weights': {'value': FakeValue(None), 'kind': 'data'},
+ 'conv_output': {'value': None, 'kind': 'data'}
+ }
+
+
+class TestConvolutionPartialInfer(unittest.TestCase):
+ def test_caffe_conv2d_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('conv_input', 'conv_node'),
+ ('conv_weights', 'conv_node'),
+ ('conv_node', 'conv_output')],
+ {'conv_output': {'is_output': True, 'shape': None},
+ 'conv_input': {'shape': np.array([1, 3, 227, 227])},
+ 'conv_weights': {'shape': np.array([64, 3, 3, 3]),
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'conv_node': {'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'conv_pad': np.array([[0, 0], [0, 0], [0, 0], [0, 0]]),
+ 'dilation': np.array([1, 1, 1, 1]), 'bias_addable': True, 'bias_term': False,
+ 'output_spatial_shape': None, 'output_shape': None,
+ 'stride': np.array([1, 1, 1, 1]), 'group': 1,
+ 'kernel_spatial_idx': np.array([2, 3]),
+ 'input_feature_channel': 1,
+ 'output_feature_channel': 0,
+ 'output': 64, 'kernel_spatial': np.array([3, 3]),
+ 'spatial_dims': np.array([2, 3]), 'channel_dims': np.array([1]),
+ 'batch_dims': np.array([0])}
+ })
+
+ conv_node = Node(graph, 'conv_node')
+ Convolution.infer(conv_node)
+ exp_shape = np.array([1, 64, 225, 225])
+ res_shape = graph.node['conv_output']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_caffe_conv2d_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('conv_input', 'conv_node'),
+ ('conv_weights', 'conv_node'),
+ ('conv_node', 'conv_output')],
+ {'conv_output': {'is_output': True, 'shape': None},
+ 'conv_input': {'shape': None},
+ 'conv_weights': {'shape': None,
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'conv_node': {'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'conv_pad': np.array([[0, 0], [0, 0], [0, 0], [0, 0]]),
+ 'dilation': np.array([1, 1, 1, 1]), 'bias_addable': True, 'bias_term': False,
+ 'output_spatial_shape': None, 'output_shape': None,
+ 'stride': np.array([1, 1, 1, 1]), 'group': 1,
+ 'output': 64, 'kernel_spatial': np.array([3, 3]),
+ 'spatial_dims': np.array([2, 3]), 'channel_dims': np.array([1]),
+ 'batch_dims': np.array([0])}
+ })
+
+ conv_node = Node(graph, 'conv_node')
+ Convolution.infer(conv_node)
+ res_shape = graph.node['conv_output']['shape']
+ self.assertIsNone(res_shape)
+
+ def test_deconv_infer_ideal(self):
+ graph = build_graph(nodes_attributes,
+ [('conv_input', 'conv_node'),
+ ('conv_weights', 'conv_node'),
+ ('conv_node', 'conv_output')],
+ {'conv_output': {'is_output': True, 'shape': None},
+ 'conv_input': {'shape': np.array([1, 21, 16, 16])},
+ 'conv_weights': {'shape': np.array([1, 21, 4, 4]),
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'conv_node': {#'spatial_dims': np.array([2, 3]), 'batch_dims': np.array([0]),
+ 'channel_dims': np.array([1]), 'bias_addable': True, 'bias_term': False,
+ 'batch_dims': np.array([0]),
+ 'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'kernel_spatial': np.array([4, 4]), 'output_spatial_shape': None,
+ 'kernel_spatial_idx': np.array([2, 3]),
+ 'input_feature_channel': 1,
+ 'output_feature_channel': 0,
+ 'output_padding': np.array([0, 0, 1, 1]),
+ 'type': 'Deconvolution', 'output': 21, 'dilation': np.array([1, 1, 1, 1]),
+ 'group': 1, 'stride': np.array([1, 1, 2, 2]), 'output_shape': None}
+ })
+
+ deconv_node = Node(graph, 'conv_node')
+
+ Convolution.infer(deconv_node)
+ res_shape = deconv_node['output_shape']
+ exp_shape = np.array([1, 21, 35, 35])
+
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ # Check that after double infer shape and pad attrs do not changes
+ Convolution.infer(deconv_node)
+
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+
+ def test_deconv_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('conv_input', 'conv_node'),
+ ('conv_weights', 'conv_node'),
+ ('conv_node', 'conv_output')],
+ {'conv_output': {'is_output': True, 'shape': None},
+ 'conv_input': {'shape': None},
+ 'conv_weights': {'shape': np.array([1, 21, 16, 16]),
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']},
+ 'conv_node': {'spatial_dims': np.array([2, 3]), 'batch_dims': np.array([0]),
+ 'channel_dims': np.array([1]),
+ 'pad_spatial_shape': np.array([[0, 0], [0, 0]]),
+ 'kernel_spatial': np.array([4, 4]), 'output_spatial_shape': None,
+ 'kernel_spatial_idx': np.array([2, 3]),
+ 'input_feature_channel': 1,
+ 'output_feature_channel': 0,
+ 'type': 'Deconvolution', 'output': 21, 'dilation': np.array([1, 1, 1, 1]),
+ 'group': 1, 'stride': np.array([1, 1, 2, 2]), 'output_shape': None}
+ })
+
+ deconv_node = Node(graph, 'conv_node')
+ Convolution.infer(deconv_node)
+ res_shape = deconv_node['output_shape']
+ self.assertIsNone(res_shape)
+
+ def test_conv_infer_set_default_attrs_nchw(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('conv_input', 'conv_node'),
+ ('conv_weights', 'conv_node'),
+ ('conv_node', 'conv_output')
+ ],
+ {
+ 'conv_output': {
+ 'is_output': True,
+ 'shape': None
+ },
+ 'conv_input': {
+ 'shape': int64_array([1, 3, 224, 224])
+ },
+ 'conv_weights': {
+ 'shape': int64_array([3, 64, 7, 7]),
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']
+ },
+ 'conv_node': {
+ 'type': 'Convolution',
+ 'bias_term': None,
+ 'stride': None,
+ 'dilation': None,
+
+ 'batch_dims': int64_array([0]),
+ 'channel_dims': int64_array([1]),
+
+ 'output_spatial_shape': None,
+
+ 'input_feature_channel': 0,
+ 'output_feature_channel': 1,
+
+ 'group': 1,
+ 'output_shape': None,
+ 'layout': 'NCHW'
+ }
+ })
+
+ conv_node = Node(graph, 'conv_node')
+ conv_output = Node(graph, 'conv_output')
+
+ Convolution.infer(conv_node)
+
+ # Check bias_term attribute
+ self.assertTrue(conv_node.has_valid('bias_term'))
+ self.assertTrue(not conv_node.bias_term)
+ # Check kernel_spatial_idx attr detection
+ self.assertTrue(conv_node.has_valid('kernel_spatial_idx'))
+ self.assertTrue(np.array_equal(int64_array([2, 3]), conv_node.kernel_spatial_idx))
+ # Check spatial_dims attr detection
+ self.assertTrue(conv_node.has_valid('spatial_dims'))
+ self.assertTrue(np.array_equal(int64_array([2, 3]), conv_node.spatial_dims))
+ # Check kernel_spatial attr detection
+ self.assertTrue(conv_node.has_valid('kernel_spatial'))
+ self.assertTrue(np.array_equal(int64_array([7, 7]), conv_node.kernel_spatial))
+ # Check output attribute
+ self.assertTrue(conv_node.has_valid('output'))
+ self.assertEqual(64, conv_node.output)
+ # Check dilation value. Should be set to default
+ self.assertTrue(conv_node.has_valid('dilation'))
+ self.assertTrue(np.array_equal(int64_array([1, 1, 1, 1]), conv_node.dilation))
+ # Check stride value. Should be set to default
+ self.assertTrue(conv_node.has_valid('stride'))
+ self.assertTrue(np.array_equal(int64_array([1, 1, 1, 1]), conv_node.stride))
+ # Check pad value. Should be set to default
+ self.assertTrue(conv_node.has_valid('pad'))
+ self.assertTrue(np.array_equal(int64_array([[0, 0], [0, 0], [0, 0], [0, 0]]), conv_node.pad))
+ # Check pad_spatial_shape
+ self.assertTrue(conv_node.has_valid('pad_spatial_shape'))
+ self.assertTrue(np.array_equal(int64_array([[0, 0], [0, 0]]), conv_node.pad_spatial_shape))
+ # Check resulting output shape
+ self.assertTrue(np.array_equal(int64_array([1, 64, 218, 218]), conv_output.shape))
+
+ def test_conv_infer_set_default_attrs_nhwc(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('conv_input', 'conv_node'),
+ ('conv_weights', 'conv_node'),
+ ('conv_node', 'conv_output')
+ ],
+ {
+ 'conv_output': {
+ 'is_output': True,
+ 'shape': None
+ },
+ 'conv_input': {
+ 'shape': int64_array([1, 224, 224, 3])
+ },
+ 'conv_weights': {
+ 'shape': int64_array([3, 64, 7, 7]),
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']
+ },
+ 'conv_node': {
+ 'type': 'Convolution',
+ 'bias_term': None,
+ 'stride': None,
+ 'dilation': None,
+
+ 'batch_dims': int64_array([0]),
+ 'channel_dims': int64_array([3]),
+
+ 'output_spatial_shape': None,
+
+ 'input_feature_channel': 0,
+ 'output_feature_channel': 1,
+
+ 'group': 1,
+ 'output_shape': None,
+ 'layout': 'NHWC'
+ }
+ })
+
+ conv_node = Node(graph, 'conv_node')
+ conv_output = Node(graph, 'conv_output')
+
+ Convolution.infer(conv_node)
+
+ # Check bias_term attribute
+ self.assertTrue(conv_node.has_valid('bias_term'))
+ self.assertTrue(not conv_node.bias_term)
+ # Check kernel_spatial_idx attr detection
+ self.assertTrue(conv_node.has_valid('kernel_spatial_idx'))
+ self.assertTrue(np.array_equal(int64_array([2, 3]), conv_node.kernel_spatial_idx))
+ # Check spatial_dims attr detection
+ self.assertTrue(conv_node.has_valid('spatial_dims'))
+ self.assertTrue(np.array_equal(int64_array([1, 2]), conv_node.spatial_dims))
+ # Check kernel_spatial attr detection
+ self.assertTrue(conv_node.has_valid('kernel_spatial'))
+ self.assertTrue(np.array_equal(int64_array([7, 7]), conv_node.kernel_spatial))
+ # Check output attribute
+ self.assertTrue(conv_node.has_valid('output'))
+ self.assertEqual(64, conv_node.output)
+ # Check dilation value. Should be set to default
+ self.assertTrue(conv_node.has_valid('dilation'))
+ self.assertTrue(np.array_equal(int64_array([1, 1, 1, 1]), conv_node.dilation))
+ # Check stride value. Should be set to default
+ self.assertTrue(conv_node.has_valid('stride'))
+ self.assertTrue(np.array_equal(int64_array([1, 1, 1, 1]), conv_node.stride))
+ # Check pad value. Should be set to default
+ self.assertTrue(conv_node.has_valid('pad'))
+ self.assertTrue(np.array_equal(int64_array([[0, 0], [0, 0], [0, 0], [0, 0]]), conv_node.pad))
+ # Check pad_spatial_shape
+ self.assertTrue(conv_node.has_valid('pad_spatial_shape'))
+ self.assertTrue(np.array_equal(int64_array([[0, 0], [0, 0]]), conv_node.pad_spatial_shape))
+ # Check resulting output shape
+ self.assertTrue(np.array_equal(int64_array([1, 218, 218, 64]), conv_output.shape))
+
+ def test_conv_infer_3D_convolution(self):
+ graph = build_graph(nodes_attributes,
+ [
+ ('conv_input', 'conv_node'),
+ ('conv_weights', 'conv_node'),
+ ('conv_node', 'conv_output')
+ ],
+ {
+ 'conv_output': {
+ 'is_output': True,
+ 'shape': None
+ },
+ 'conv_input': {
+ 'shape': int64_array([1, 3, 16, 224, 224])
+ },
+ 'conv_weights': {
+ 'shape': int64_array([3, 64, 1, 7, 7]),
+ 'dim_attrs': ['spatial_dims', 'channel_dims', 'batch_dims', 'axis']
+ },
+ 'conv_node': {
+ 'type': 'Convolution',
+ 'bias_term': None,
+ 'stride': None,
+ 'dilation': None,
+
+ 'batch_dims': int64_array([0]),
+ 'channel_dims': int64_array([1]),
+
+ 'output_spatial_shape': None,
+
+ 'input_feature_channel': 0,
+ 'output_feature_channel': 1,
+
+ 'group': 1,
+ 'output_shape': None,
+ 'layout': 'NCHW'
+ }
+ })
+
+ conv_node = Node(graph, 'conv_node')
+ conv_output = Node(graph, 'conv_output')
+
+ Convolution.infer(conv_node)
+
+ # Check bias_term attribute
+ self.assertTrue(conv_node.has_valid('bias_term'))
+ self.assertTrue(not conv_node.bias_term)
+ # Check kernel_spatial_idx attr detection
+ self.assertTrue(conv_node.has_valid('kernel_spatial_idx'))
+ self.assertTrue(np.array_equal(int64_array([2, 3, 4]), conv_node.kernel_spatial_idx))
+ # Check spatial_dims attr detection
+ self.assertTrue(conv_node.has_valid('spatial_dims'))
+ self.assertTrue(np.array_equal(int64_array([2, 3, 4]), conv_node.spatial_dims))
+ # Check kernel_spatial attr detection
+ self.assertTrue(conv_node.has_valid('kernel_spatial'))
+ self.assertTrue(np.array_equal(int64_array([1, 7, 7]), conv_node.kernel_spatial))
+ # Check output attribute
+ self.assertTrue(conv_node.has_valid('output'))
+ self.assertEqual(64, conv_node.output)
+ # Check dilation value. Should be set to default
+ self.assertTrue(conv_node.has_valid('dilation'))
+ self.assertTrue(np.array_equal(int64_array([1, 1, 1, 1, 1]), conv_node.dilation))
+ # Check stride value. Should be set to default
+ self.assertTrue(conv_node.has_valid('stride'))
+ self.assertTrue(np.array_equal(int64_array([1, 1, 1, 1, 1]), conv_node.stride))
+ # Check pad value. Should be set to default
+ self.assertTrue(conv_node.has_valid('pad'))
+ self.assertTrue(np.array_equal(int64_array([[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]), conv_node.pad))
+ # Check pad_spatial_shape
+ self.assertTrue(conv_node.has_valid('pad_spatial_shape'))
+ self.assertTrue(np.array_equal(int64_array([[0, 0], [0, 0], [0, 0]]), conv_node.pad_spatial_shape))
+ # Check resulting output shape
+ self.assertTrue(np.array_equal(int64_array([1, 64, 16, 218, 218]), conv_output.shape))
diff --git a/model-optimizer/mo/ops/crop_test.py b/model-optimizer/mo/ops/crop_test.py
new file mode 100644
index 000000000..9eb541271
--- /dev/null
+++ b/model-optimizer/mo/ops/crop_test.py
@@ -0,0 +1,187 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.utils import int64_array
+from mo.graph.graph import Node
+from mo.ops.crop import Crop
+from mo.utils.unittest.graph import build_graph
+
+
+class TestCropPartialInfer(unittest.TestCase):
+ @staticmethod
+ def _create_graph_type1():
+ nodes_attributes = {'crop_input': {'shape': None, 'value': None, 'kind': 'data'},
+ 'crop_node': {'type': 'Crop', 'kind': 'op'},
+ 'crop_output': {'shape': None, 'value': None, 'kind': 'data'}
+ }
+ return build_graph(nodes_attributes,
+ [
+ ('crop_input', 'crop_node'), ('crop_node', 'crop_output')
+ ],
+ {
+ 'crop_input': {'shape': int64_array([1, 3, 224, 224])},
+ 'crop_node': {'axis': int64_array([2, 3]),
+ 'crop_begin': int64_array([10, 15]),
+ 'crop_end': int64_array([10, 15])
+ },
+ })
+
+ @staticmethod
+ def _create_graph_type2():
+ nodes_attributes = {'crop_input': {'shape': None, 'value': None, 'kind': 'data'},
+ 'crop_node': {'type': 'Crop', 'kind': 'op'},
+ 'crop_output': {'shape': None, 'value': None, 'kind': 'data'}
+ }
+ return build_graph(nodes_attributes,
+ [
+ ('crop_input', 'crop_node'), ('crop_node', 'crop_output')
+ ],
+ {
+ 'crop_input': {'shape': int64_array([1, 3, 224, 224])},
+ 'crop_node': {'axis': int64_array([2, 3]), 'dim': int64_array([100, 150])},
+ })
+
+ @staticmethod
+ def _create_graph_type3():
+ nodes_attributes = {'crop_input': {'shape': None, 'value': None, 'kind': 'data'},
+ 'crop_input2': {'shape': None, 'value': None, 'kind': 'data'},
+ 'crop_node': {'type': 'Crop', 'kind': 'op'},
+ 'crop_output': {'shape': None, 'value': None, 'kind': 'data'}
+ }
+ return build_graph(nodes_attributes,
+ [
+ ('crop_input', 'crop_node'), ('crop_input2', 'crop_node'), ('crop_node', 'crop_output')
+ ],
+ {
+ 'crop_input': {'shape': int64_array([1, 3, 224, 224])},
+ 'crop_input2': {'shape': int64_array([1, 3, 100, 150])},
+ 'crop_node': {'axis': 2, 'offset': int64_array([10, 15])},
+ })
+
+ def test_crop_type1_infer(self):
+ graph = self._create_graph_type1()
+
+ crop_node = Node(graph, 'crop_node')
+ Crop.infer(crop_node)
+
+ exp_shape = int64_array([1, 3, 204, 194])
+ res_shape = graph.node['crop_output']['shape']
+
+ self.assertTrue(np.array_equal(exp_shape, res_shape),
+ 'shapes do not match expected: {} and given: {}'.format(exp_shape, res_shape))
+
+ def test_crop_type1_infer_neg1(self):
+ graph = self._create_graph_type1()
+
+ crop_node = Node(graph, 'crop_node')
+ crop_node['axis'] = None
+
+ Crop.infer(crop_node)
+ self.assertIsNone(crop_node.out_node().shape)
+
+ def test_crop_type1_infer_neg2(self):
+ graph = self._create_graph_type1()
+
+ crop_node = Node(graph, 'crop_node')
+ crop_node['crop_begin'] = int64_array([1, 2, 3])
+
+ Crop.infer(crop_node)
+ self.assertIsNone(crop_node.out_node().shape)
+
+ def test_crop_type2_infer(self):
+ graph = self._create_graph_type2()
+
+ crop_node = Node(graph, 'crop_node')
+ Crop.infer(crop_node)
+
+ exp_shape = int64_array([1, 3, 100, 150])
+ res_shape = graph.node['crop_output']['shape']
+
+ self.assertTrue(np.array_equal(exp_shape, res_shape),
+ 'shapes do not match expected: {} and given: {}'.format(exp_shape, res_shape))
+
+ def test_crop_type2_infer_neg1(self):
+ graph = self._create_graph_type2()
+
+ crop_node = Node(graph, 'crop_node')
+ crop_node['dim'] = int64_array([1, 2, 3])
+
+ Crop.infer(crop_node)
+ self.assertIsNone(crop_node.out_node().shape)
+
+ def test_crop_type2_infer_neg2(self):
+ graph = self._create_graph_type2()
+
+ crop_node = Node(graph, 'crop_node')
+ crop_node['dim'] = None
+ crop_node['crop_begin'] = None
+
+ Crop.infer(crop_node)
+ self.assertIsNone(crop_node.out_node().shape)
+
+ def test_crop_type3_infer(self):
+ graph = self._create_graph_type3()
+
+ crop_node = Node(graph, 'crop_node')
+ Crop.infer(crop_node)
+
+ exp_shape = int64_array([1, 3, 100, 150])
+ res_shape = graph.node['crop_output']['shape']
+
+ self.assertTrue(np.array_equal(exp_shape, res_shape),
+ 'shapes do not match expected: {} and given: {}'.format(exp_shape, res_shape))
+
+ def test_crop_type3_infer_neg1(self):
+ graph = self._create_graph_type3()
+
+ crop_node = Node(graph, 'crop_node')
+ crop_input2 = Node(graph, 'crop_input2')
+ crop_input2.shape = None
+
+ Crop.infer(crop_node)
+ self.assertIsNone(crop_node.out_node().shape)
+
+ def test_crop_type3_infer_neg2(self):
+ graph = self._create_graph_type3()
+
+ crop_node = Node(graph, 'crop_node')
+ crop_node['axis'] = None
+
+ Crop.infer(crop_node)
+ self.assertIsNone(crop_node.out_node().shape)
+
+ def test_crop_type3_infer_neg3(self):
+ graph = self._create_graph_type3()
+
+ crop_node = Node(graph, 'crop_node')
+ crop_node['offset'] = None
+
+ Crop.infer(crop_node)
+ self.assertIsNone(crop_node.out_node().shape)
+
+ def test_crop_type3_infer_neg4(self):
+ graph = self._create_graph_type3()
+
+ crop_node = Node(graph, 'crop_node')
+ crop_input2 = Node(graph, 'crop_input2')
+ crop_input2.shape = int64_array([1, 4, 423, 563])
+
+ Crop.infer(crop_node)
+ self.assertIsNone(crop_node.out_node().shape)
diff --git a/model-optimizer/mo/ops/flatten_onnx_test.py b/model-optimizer/mo/ops/flatten_onnx_test.py
new file mode 100644
index 000000000..1e68fbb69
--- /dev/null
+++ b/model-optimizer/mo/ops/flatten_onnx_test.py
@@ -0,0 +1,65 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+from generator import generator, generate
+
+from mo.graph.graph import Node
+from mo.ops.flatten_onnx import FlattenONNX
+from mo.utils.unittest.graph import build_graph
+
+
+@generator
+class TestFlattenONNXOp(unittest.TestCase):
+ # There are tests for InnerProduct.infer in mo/front/common/partial_infer/inner_product_test.py
+ nodes_attributes = {
+ 'data_1': {
+ 'kind': 'data',
+ 'shape': np.array([1, 3, 224, 224])
+ },
+ 'flatten': {
+ 'type': 'Reshape',
+ 'axis': None,
+ 'kind': 'op',
+ },
+ 'data_2': {
+ 'kind': 'data',
+ 'shape': None,
+ }
+ }
+
+ def _create_graph_with_flatten(self, axis):
+ graph = build_graph(self.nodes_attributes,
+ [('data_1', 'flatten'),
+ ('flatten', 'data_2')],
+ {'flatten': {'axis': axis}})
+ return graph
+
+ @generate(*[(0, [1, 3 * 224 * 224]),
+ (1, [1, 3 * 224 * 224]),
+ (2, [3, 224 * 224]),
+ (3, [3 * 224, 224]),
+ (4, [3 * 224 * 224, 1]),
+ ])
+ def test_flatten_infer_1(self, axis, ref):
+ graph = self._create_graph_with_flatten(axis)
+ flatten_node = Node(graph, 'flatten')
+
+ FlattenONNX.infer(flatten_node)
+
+ self.assertTrue(np.array_equal(flatten_node.out_node().shape, np.array(ref)))
diff --git a/model-optimizer/mo/ops/flatten_test.py b/model-optimizer/mo/ops/flatten_test.py
new file mode 100644
index 000000000..9d584010a
--- /dev/null
+++ b/model-optimizer/mo/ops/flatten_test.py
@@ -0,0 +1,62 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.ops.flatten import Flatten
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'value': None, 'kind': 'data'},
+ 'flatten_1': {'type': 'Flatten', 'value': None, 'kind': 'op'},
+ 'node_2': {'value': None, 'kind': 'data'}
+ }
+
+
+class TestFlattenPartialInfer(unittest.TestCase):
+ def test_flatten_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'flatten_1'),
+ ('flatten_1', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': np.array([1, 3 * 256 * 256])},
+ 'node_1': {'shape': np.array([1, 3, 256, 256])},
+ 'flatten_1': {'axis': 1, 'dim': []}
+ })
+
+ flatten_node = Node(graph, 'flatten_1')
+
+ Flatten.infer(flatten_node)
+ exp_shape = np.array([1, 3 * 256 * 256])
+ res_shape = graph.node['node_2']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_flatten_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'flatten_1'),
+ ('flatten_1', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': None},
+ 'flatten_1': {'axis': 1}
+ })
+
+ flatten_node = Node(graph, 'flatten_1')
+
+ Flatten.infer(flatten_node)
+ res_shape = graph.node['node_2']['shape']
+ self.assertIsNone(res_shape)
diff --git a/model-optimizer/mo/ops/inner_product_test.py b/model-optimizer/mo/ops/inner_product_test.py
new file mode 100644
index 000000000..22d3c4af8
--- /dev/null
+++ b/model-optimizer/mo/ops/inner_product_test.py
@@ -0,0 +1,48 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.front.common.partial_infer.inner_product import caffe_inner_product
+from mo.ops.inner_product import InnerProduct
+from mo.utils.unittest.graph import build_graph
+
+
+class TestInnerProductOp(unittest.TestCase):
+ # There are tests for InnerProduct.infer in mo/front/common/partial_infer/inner_product_test.py
+ nodes_attributes = {
+ 'node_1': {
+ 'shape': np.array([227, 5, 2, 1])
+ },
+ 'fc_node': {
+ },
+ 'node_3': {
+ 'kind': 'data'
+ }
+ }
+
+ def test_concat_op(self):
+ graph = build_graph(self.nodes_attributes,
+ [
+ ('node_1', 'fc_node'),
+ ('fc_node', 'node_3')
+ ])
+ fc_node = InnerProduct(graph, self.nodes_attributes['fc_node']).add_node()
+ self.assertEqual(fc_node.type, 'FullyConnected')
+ self.assertEqual(fc_node.op, 'FullyConnected')
+ self.assertEqual(fc_node.infer, caffe_inner_product)
diff --git a/model-optimizer/mo/ops/pad_test.py b/model-optimizer/mo/ops/pad_test.py
new file mode 100644
index 000000000..0013ed358
--- /dev/null
+++ b/model-optimizer/mo/ops/pad_test.py
@@ -0,0 +1,95 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.ops.pad import Pad
+from mo.utils.unittest.graph import build_graph
+
+
+class TestPadONNXOp(unittest.TestCase):
+ # There are tests for InnerProduct.infer in mo/front/common/partial_infer/inner_product_test.py
+ node_attrs = {
+ 'data_in': {
+ 'kind': 'data',
+ 'shape': np.array([1, 3, 100, 200])
+ },
+ # optional input for one of the two flavors of pad op
+ 'data_pads': {
+ 'kind': 'data',
+ 'value': np.array([[0, 0], [0, 0], [1, 3], [2, 4]], dtype=np.int64),
+ 'shape': np.array([2, 4], dtype=np.int64)
+ },
+ 'pad': {
+ 'op': 'Pad',
+ 'kind': 'op',
+ 'pads': None,
+ },
+ 'data_out': {
+ 'kind': 'data',
+ 'shape': None,
+ }
+ }
+
+ edge_attrs = [
+ ('data_in', 'pad'),
+ ('pad', 'data_out')
+ ]
+
+ def test_one_input(self):
+ graph = build_graph(
+ self.node_attrs,
+ self.edge_attrs,
+ {'pad': {'pads': np.array([[0, 0], [0, 0], [1, 3], [2, 4]], dtype=np.int64)}},
+ nodes_with_edges_only=True,
+ )
+ pad_node = Node(graph, 'pad')
+ Pad.infer(pad_node)
+ self.assertTrue(np.array_equal(Node(graph, 'data_out').shape, np.array([1, 3, 100 + 1 + 3, 200 + 2 + 4])))
+
+ def test_two_inputs(self):
+ graph = build_graph(
+ self.node_attrs,
+ self.edge_attrs + [('data_pads', 'pad')],
+ nodes_with_edges_only=True,
+ )
+ pad_node = Node(graph, 'pad')
+ Pad.infer(pad_node)
+ self.assertTrue(np.array_equal(Node(graph, 'data_out').shape, np.array([1, 3, 100 + 1 + 3, 200 + 2 + 4])))
+
+ def test_one_input_and_no_pads(self):
+ graph = build_graph(
+ self.node_attrs,
+ self.edge_attrs,
+ nodes_with_edges_only=True,
+ )
+ pad_node = Node(graph, 'pad')
+ with self.assertRaisesRegex(AssertionError, ".*pads attribute is missing.*"):
+ Pad.infer(pad_node)
+
+ def test_two_inputs_and_pads(self):
+ graph = build_graph(
+ self.node_attrs,
+ self.edge_attrs + [('data_pads', 'pad')],
+ {'pad': {'pads': np.array([[0, 0], [0, 0], [1, 3], [2, 4]], dtype=np.int64)}},
+ nodes_with_edges_only=True,
+ )
+ pad_node = Node(graph, 'pad')
+ with self.assertRaisesRegex(AssertionError, ".*unexpected additional input argument.*"):
+ Pad.infer(pad_node)
diff --git a/model-optimizer/mo/ops/permute_test.py b/model-optimizer/mo/ops/permute_test.py
new file mode 100644
index 000000000..cf26cc76f
--- /dev/null
+++ b/model-optimizer/mo/ops/permute_test.py
@@ -0,0 +1,96 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import itertools
+import unittest
+
+import numpy as np
+from generator import generator, generate
+
+from mo.graph.graph import Node
+from mo.ops.permute import Permute
+from mo.utils.unittest.graph import build_graph
+
+
+@generator
+class TestPermuteOp(unittest.TestCase):
+ nodes_attributes = {
+ 'data_1': {
+ 'kind': 'data',
+ 'shape': np.array([1, 3, 224, 224])
+ },
+ 'transpose': {
+ 'type': 'Permute',
+ 'order': None,
+ 'reverse_order': False,
+ 'kind': 'op',
+ },
+ 'data_2': {
+ 'kind': 'data',
+ 'shape': None,
+ }
+ }
+
+ def _create_graph_with_transpose(self, order):
+ graph = build_graph(self.nodes_attributes,
+ [('data_1', 'transpose'),
+ ('transpose', 'data_2')],
+ {'transpose': {'order': order}})
+ return graph
+
+ @generate(*[list(order) for order in list(itertools.permutations(np.arange(4)))])
+ def test_transpose_infer_1(self, order):
+ graph = self._create_graph_with_transpose(order)
+ transpose_node = Node(graph, 'transpose')
+
+ Permute.infer(transpose_node)
+
+ ref = [transpose_node.in_node().shape[i] for i in order]
+ self.assertTrue(np.array_equal(transpose_node.out_node().shape, np.array(ref)))
+
+ def test_transpose_infer_2(self):
+ order = None
+ graph = self._create_graph_with_transpose(order)
+ transpose_node = Node(graph, 'transpose')
+ transpose_node['reverse_order'] = True
+
+ Permute.infer(transpose_node)
+
+ ref = np.array([x for x in reversed(transpose_node.in_node().shape)])
+ self.assertTrue(np.array_equal(transpose_node.out_node().shape, ref),
+ "Shapes are not the same: {} and {}".format(transpose_node.out_node().shape, ref))
+
+ def test_transpose_infer_neg_1(self):
+ order = np.array([0, 1, 2, 3])
+ graph = self._create_graph_with_transpose(order)
+ transpose_node = Node(graph, 'transpose')
+ transpose_node['reverse_order'] = True
+
+ Permute.infer(transpose_node)
+
+ ref = None
+ self.assertTrue(transpose_node.out_node().shape is None, "Output shape should be None")
+
+ def test_transpose_infer_neg_2(self):
+ order = None
+ graph = self._create_graph_with_transpose(order)
+ transpose_node = Node(graph, 'transpose')
+ transpose_node['reverse_order'] = False
+
+ Permute.infer(transpose_node)
+
+ ref = None
+ self.assertTrue(transpose_node.out_node().shape is None, "Output shape should be None")
diff --git a/model-optimizer/mo/ops/pooling_test.py b/model-optimizer/mo/ops/pooling_test.py
new file mode 100644
index 000000000..ea11b72a7
--- /dev/null
+++ b/model-optimizer/mo/ops/pooling_test.py
@@ -0,0 +1,122 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.ops.pooling import Pooling
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'node_1': {'value': None, 'kind': 'data'},
+ 'pool': {'type': 'Pooling', 'value': None, 'kind': 'op'},
+ 'node_2': {'value': None, 'kind': 'data'},
+ }
+
+
+class TestPoolingPartialInfer(unittest.TestCase):
+ def test_pooling_infer(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'pool'),
+ ('pool', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 256, 256])},
+ 'pool': {'window': np.array([1, 1, 1, 1]), 'stride': np.array([1, 1, 2, 2]),
+ 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]]),
+ 'pad_spatial_shape': np.array([[3, 3], [3, 3]]),
+ 'pool_method': 'avg', 'exclude_pad': 'false', 'global_pool': 0,
+ 'output_spatial_shape': None, 'output_shape': None,
+ 'kernel_spatial': np.array([3, 3]), 'spatial_dims': np.array([2, 3]),
+ 'channel_dims': np.array([1]), 'batch_dims': np.array([0]),
+ 'pooling_convention': 'full'}
+ })
+
+ pool_node = Node(graph, 'pool')
+
+ Pooling.infer(pool_node)
+ exp_shape = np.array([1, 3, 131, 131])
+ res_shape = graph.node['node_2']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_pooling_infer_decrement_input_spatial(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'pool'),
+ ('pool', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 224, 224])},
+ 'pool': {'window': np.array([1, 1, 1, 1]), 'stride': np.array([1, 1, 3, 3]),
+ 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]]),
+ 'pad_spatial_shape': np.array([[1, 1], [1, 1]]),
+ 'pool_method': 'avg', 'exclude_pad': 'false', 'global_pool': 0,
+ 'output_spatial_shape': None, 'output_shape': None,
+ 'kernel_spatial': np.array([3, 3]), 'spatial_dims': np.array([2, 3]),
+ 'channel_dims': np.array([1]), 'batch_dims': np.array([0]),
+ 'pooling_convention': 'full'}
+ })
+
+ pool_node = Node(graph, 'pool')
+
+ Pooling.infer(pool_node)
+ exp_shape = np.array([1, 3, 75, 75])
+ res_shape = graph.node['node_2']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_pooling_infer_no_convention(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'pool'),
+ ('pool', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': np.array([1, 3, 256, 256])},
+ 'pool': {'window': np.array([1, 1, 1, 1]), 'stride': np.array([1, 1, 2, 2]),
+ 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]]),
+ 'pad_spatial_shape': np.array([[3, 3], [3, 3]]),
+ 'pool_method': 'avg', 'exclude_pad': 'false', 'global_pool': 0,
+ 'output_spatial_shape': None, 'output_shape': None,
+ 'kernel_spatial': np.array([3, 3]), 'spatial_dims': np.array([2, 3]),
+ 'channel_dims': np.array([1]), 'batch_dims': np.array([0])}
+ })
+
+ pool_node = Node(graph, 'pool')
+
+ Pooling.infer(pool_node)
+ exp_shape = np.array([1, 3, 130, 130])
+ res_shape = graph.node['node_2']['shape']
+ for i in range(0, len(exp_shape)):
+ self.assertEqual(exp_shape[i], res_shape[i])
+
+ def test_pooling_infer_no_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('node_1', 'pool'),
+ ('pool', 'node_2')],
+ {'node_2': {'is_output': True, 'shape': None},
+ 'node_1': {'shape': None},
+ 'pool': {'window': np.array([1, 1, 1, 1]), 'stride': np.array([1, 1, 2, 2]),
+ 'pad': np.array([[0, 0], [0, 0], [3, 3], [3, 3]]),
+ 'pad_spatial_shape': np.array([[3, 3], [3, 3]]),
+ 'pool_method': 'avg', 'exclude_pad': 'false',
+ 'output_spatial_shape': None, 'output_shape': None,
+ 'kernel_spatial': np.array([3, 3]), 'spatial_dims': np.array([2, 3]),
+ 'channel_dims': np.array([1]), 'batch_dims': np.array([0]),
+ 'pooling_convention': 'full'}
+ })
+
+ pool_node = Node(graph, 'pool')
+ Pooling.infer(pool_node)
+ res_shape = graph.node['node_2']['shape']
+ self.assertIsNone(res_shape)
diff --git a/model-optimizer/mo/ops/power_test.py b/model-optimizer/mo/ops/power_test.py
new file mode 100644
index 000000000..e0a3b97a3
--- /dev/null
+++ b/model-optimizer/mo/ops/power_test.py
@@ -0,0 +1,100 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.ops.power import Power
+from mo.utils.unittest.graph import build_graph
+
+
+class TestPowerOp(unittest.TestCase):
+ @staticmethod
+ def create_graph(single_input=True):
+ nodes_attributes = {
+ 'input1': {
+ 'kind': 'data',
+ 'shape': np.array([1, 3, 224, 224]),
+ 'value': None,
+ },
+ 'input2': {
+ 'kind': 'data',
+ 'shape': np.array([]),
+ 'value': np.array(1.0),
+ },
+ 'power': {
+ 'kind': 'op',
+ 'shape': np.array([1, 3, 224, 224]),
+ },
+ 'power_data': {
+ 'kind': 'data',
+ 'shape': None,
+ },
+ }
+ if single_input:
+ return build_graph(nodes_attributes,
+ [
+ ('input1', 'power'),
+ ('power', 'power_data')
+ ])
+ else:
+ return build_graph(nodes_attributes,
+ [
+ ('input1', 'power'),
+ ('input2', 'power'),
+ ('power', 'power_data')
+ ])
+
+ def test_power_single_input_infer1(self):
+ graph = self.create_graph(single_input=True)
+ graph.graph['layout'] = 'NCHW'
+ power_node = Node(graph, 'power')
+ power_node['power'] = 1.0
+
+ Power.infer(power_node)
+
+ self.assertTrue(np.array_equal(power_node.out_node().shape, power_node.in_node(0).shape))
+
+ def test_power_two_input_infer1(self):
+ graph = self.create_graph(single_input=False)
+ graph.graph['layout'] = 'NCHW'
+ power_node = Node(graph, 'power')
+
+ Power.infer(power_node)
+
+ self.assertTrue(np.array_equal(power_node.out_node().shape, power_node.in_node(0).shape))
+
+ def test_power_two_input_infer2(self):
+ graph = self.create_graph(single_input=False)
+ power_node = Node(graph, 'power')
+ input2 = Node(graph, 'input2')
+ input2.value = np.ones((1, 2, 3))
+
+ Power.infer(power_node)
+
+ self.assertIsNone(power_node.out_node().shape)
+
+ def test_power_two_input_infer3(self):
+ graph = self.create_graph(single_input=False)
+ power_node = Node(graph, 'power')
+ input2 = Node(graph, 'input2')
+ input2.value = None
+
+ Power.infer(power_node)
+
+ self.assertIsNone(power_node.out_node().shape)
diff --git a/model-optimizer/mo/ops/slice_test.py b/model-optimizer/mo/ops/slice_test.py
new file mode 100644
index 000000000..2061e30b3
--- /dev/null
+++ b/model-optimizer/mo/ops/slice_test.py
@@ -0,0 +1,117 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import unittest
+
+import numpy as np
+from generator import generator
+
+from mo.graph.graph import Node
+from mo.ops.slice import Slice
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {
+ 'data_1': {
+ 'kind': 'data',
+ 'shape': None,
+ 'value': None,
+ },
+ 'begin': {
+ 'kind': 'data',
+ 'shape': None,
+ 'value': None,
+ },
+ 'size': {
+ 'kind': 'data',
+ 'shape': None,
+ 'value': None,
+ },
+ 'slice': {
+ 'op': 'Slice',
+ 'axis': None,
+ 'start': None,
+ 'end': None,
+ 'kind': 'op',
+ },
+ 'data_2': {
+ 'kind': 'data',
+ 'shape': None,
+ 'value': None,
+ }
+}
+
+
+@generator
+class TestSliceOp(unittest.TestCase):
+ def test_slice_infer_constant(self):
+ # Testing constant path case
+ graph = build_graph(nodes_attributes,
+ [('data_1', 'slice'),
+ ('begin', 'slice'),
+ ('size', 'slice'),
+ ('slice', 'data_2')],
+ {'data_1': {'shape': np.array([4]), 'value': np.array([1, 3, 224, 224])},
+ 'slice': {'start': np.array([1]), 'end': np.array([2])},
+ 'size': {'value': np.array([1])},
+ 'begin': {'value': np.array([1])}})
+
+ slice_node = Node(graph, 'slice')
+ Slice.infer(slice_node)
+
+ self.assertTrue(np.array_equal(slice_node.out_node().value, np.array([3])))
+ self.assertTrue(np.array_equal(slice_node.out_node().shape, np.array([1])))
+ self.assertTrue(np.array_equal(slice_node['slices'], np.array([slice(1, 2, 1)])))
+
+ 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'),
+ ('begin', 'slice'),
+ ('size', 'slice'),
+ ('slice', 'data_2')],
+ {'data_1': {'shape': np.array([4, 5, 6])},
+ 'slice': {'start': np.array([1, 2]),
+ 'end': np.array([4, 3])},
+ 'size': {'value': np.array([3, 1])},
+ 'begin': {'value': np.array([1, 2])}})
+
+ 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])))
+ self.assertTrue(np.array_equal(slice_node['slices'], np.array([slice(1, 4, 1), slice(2, 3, 1), slice(0, 6, 1)])))
+
+ def test_slice_infer_multiply_params(self):
+ # Test case when size[i] == -1 (that means all
+ # remaining elements in dimension i are included in the slice)
+ graph = build_graph(nodes_attributes,
+ [('data_1', 'slice'),
+ ('begin', 'slice'),
+ ('size', 'slice'),
+ ('slice', 'data_2')],
+ {'data_1': {'shape': np.array([4, 5, 6])},
+ 'slice': {'start': np.array([1, 2]),
+ 'end': np.array([4, 1])},
+ 'size': {'value': np.array([3, -1])},
+ 'begin': {'value': np.array([1, 2])}})
+
+ 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, 3, 6])))
+ self.assertTrue(np.array_equal(slice_node['slices'], np.array([slice(1, 4, 1), slice(2, 5, 1), slice(0, 6, 1)])))
diff --git a/model-optimizer/mo/ops/tile_test.py b/model-optimizer/mo/ops/tile_test.py
new file mode 100644
index 000000000..af0d189cb
--- /dev/null
+++ b/model-optimizer/mo/ops/tile_test.py
@@ -0,0 +1,196 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.ops.tile import Tile
+from mo.utils.unittest.graph import build_graph
+
+nodes_attributes = {'data': {'value': None, 'shape': np.array([10, 20, 30, 40]), 'kind': 'data'},
+ 'tile_values': {'value': None, 'shape': np.array([4]), 'kind': 'data'},
+ 'tile': {'type': 'Tile', 'kind': 'op'},
+ 'tile_out': {'value': None, 'shape': None, 'kind': 'data'},
+ }
+
+
+class TestTileInfer(unittest.TestCase):
+ def test_tile_infer_correct(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile_values': {'value': np.array([7, 1, 1, 1])}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertTrue(np.all(np.array([70, 20, 30, 40]) == graph.node['tile_out']['shape']))
+ self.assertEqual(tile_node.axis, 0)
+ self.assertEqual(tile_node.tiles, 7)
+
+ def test_tile_infer_correct_2(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile_values': {'value': np.array([1, 7, 1, 1])}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertTrue(np.all(np.array([10, 140, 30, 40]) == graph.node['tile_out']['shape']))
+ self.assertEqual(tile_node.axis, 1)
+ self.assertEqual(tile_node.tiles, 7)
+
+ def test_tile_infer_correct_2d_tensor(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'data': {'shape': np.array([3, 7])},
+ 'tile_values': {'value': np.array([5, 1])}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertTrue(np.all(np.array([15, 7]) == graph.node['tile_out']['shape']))
+ self.assertEqual(tile_node.axis, 0)
+ self.assertEqual(tile_node.tiles, 5)
+
+ def test_tile_infer_all_ones(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile_values': {'value': np.array([1, 1, 1, 1])}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertTrue(np.all(np.array([10, 20, 30, 40]) == graph.node['tile_out']['shape']))
+ self.assertEqual(tile_node.axis, 0)
+ self.assertEqual(tile_node.tiles, 1)
+
+ def test_tile_infer_two_non_one(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile_values': {'value': np.array([2, 1, 1, 2])}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertIsNone(graph.node['tile']['type'])
+ self.assertTrue(np.all(np.array([20, 20, 30, 80]) == graph.node['tile_out']['shape']))
+ self.assertFalse(tile_node.has_and_set('axis'))
+ self.assertFalse(tile_node.has_and_set('tiles'))
+
+ def test_tile_infer_three_non_one(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile_values': {'value': np.array([2, 1, 5, 2])}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertIsNone(graph.node['tile']['type'])
+ self.assertTrue(np.all(np.array([20, 20, 150, 80]) == graph.node['tile_out']['shape']))
+
+ def test_tile_infer_none_input_shape(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'data': {'shape': None},
+ 'tile_values': {'value': np.array([1, 7, 1, 1])}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertIsNone(graph.node['tile_out']['shape'])
+
+ def test_tile_infer_values_test(self):
+ input_data = np.arange(-30, 60, 0.25).reshape([2, 4, 3, -1])
+ tile_values = np.array([3, 1, 1, 1])
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'data': {'shape': input_data.shape, 'value': input_data},
+ 'tile_values': {'value': tile_values}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertTrue(np.all(np.tile(input_data, tile_values) == graph.node['tile_out']['value']))
+ self.assertEqual(tile_node.axis, 0)
+ self.assertEqual(tile_node.tiles, 3)
+
+ def test_tile_infer_values_const_propagation(self):
+ """
+ Test for constant propagation even if tile with multiple tile indices is not supported
+ """
+ input_data = np.arange(-30, 60, 0.25).reshape([2, 4, 3, -1])
+ tile_values = np.array([4, 3, 2, 5])
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'data': {'shape': input_data.shape, 'value': input_data},
+ 'tile_values': {'value': tile_values}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertTrue(np.all(np.tile(input_data, tile_values) == graph.node['tile_out']['value']))
+ self.assertIsNone(tile_node.type)
+
+ def test_tile_infer_undefined_tile_values(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile_values': {'value': None}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertIsNone(graph.node['tile_out']['shape'])
+
+ def test_tile_infer_shapes_mismatch(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile_values', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile_values': {'value': np.array([1, 2, 1]), 'shape': np.array([3])}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertIsNone(graph.node['tile_out']['shape'])
+
+ def test_tile_infer_one_input_correct(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile': {'axis': 1, 'tiles': 7}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertTrue(np.all(np.array([10, 140, 30, 40]) == graph.node['tile_out']['shape']))
+ self.assertEqual(tile_node.axis, 1)
+ self.assertEqual(tile_node.tiles, 7)
+
+ def test_tile_infer_one_input_correct_missing_axis(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile': {'tiles': 7}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertIsNone(graph.node['tile_out']['shape'])
+
+ def test_tile_infer_one_input_correct_missing_tiles(self):
+ graph = build_graph(nodes_attributes,
+ [('data', 'tile'),
+ ('tile', 'tile_out')],
+ {'tile': {'axis': 1}})
+ tile_node = Node(graph, 'tile')
+ Tile.infer(tile_node)
+ self.assertIsNone(graph.node['tile_out']['shape'])
diff --git a/model-optimizer/mo/ops/unsqueeze_test.py b/model-optimizer/mo/ops/unsqueeze_test.py
new file mode 100644
index 000000000..06d25b057
--- /dev/null
+++ b/model-optimizer/mo/ops/unsqueeze_test.py
@@ -0,0 +1,67 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+from generator import generator
+
+from mo.graph.graph import Node
+from mo.ops.unsqueeze import Unsqueeze
+from mo.utils.unittest.graph import build_graph, compare_graphs
+
+
+@generator
+class TestUnsqueezeOp(unittest.TestCase):
+ nodes_attributes = {
+ 'data_1': {
+ 'kind': 'data',
+ 'shape': None,
+ 'value': None,
+ },
+ 'unsq': {
+ 'op': 'Unsqueeze',
+ 'kind': 'op',
+ 'unsqueeze_dims': None,
+ },
+ 'data_2': {
+ 'kind': 'data',
+ 'shape': None,
+ 'value': None,
+ }
+ }
+
+ def test_unsqueeze_infer(self):
+ graph = build_graph(self.nodes_attributes,
+ [('data_1', 'unsq'),
+ ('unsq', 'data_2')],
+ {'data_1': {'shape': np.array([1, 3, 64, 64])},
+ 'unsq': {'unsqueeze_dims': np.array([0, 4])}
+ })
+
+ graph_ref = build_graph(self.nodes_attributes,
+ [('data_1', 'unsq'),
+ ('unsq', 'data_2')],
+ {'data_1': {'shape': np.array([1, 3, 64, 64])},
+ 'unsq': {'unsqueeze_dims': np.array([0, 4])},
+ 'data_2': {'shape': np.array([1, 1, 3, 64, 1, 64])}
+ })
+
+ unsqueeze_node = Node(graph, 'unsq')
+ Unsqueeze.infer(unsqueeze_node)
+
+ (flag, resp) = compare_graphs(graph, graph_ref, 'data_2')
+ self.assertTrue(flag, resp)
diff --git a/model-optimizer/mo/pipeline/common_test.py b/model-optimizer/mo/pipeline/common_test.py
new file mode 100644
index 000000000..a87770083
--- /dev/null
+++ b/model-optimizer/mo/pipeline/common_test.py
@@ -0,0 +1,241 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from generator import generator, generate
+
+from mo.graph.graph import Node
+from mo.pipeline.common import determined_sort, get_fw_tensor_debug_info, get_sorted_outputs
+from mo.utils.unittest.graph import build_graph_with_edge_attrs
+
+
+@generator
+class TestTopologicalSort(unittest.TestCase):
+ @generate(
+ [('A', 'Ad', {'out': 0}),
+ ('Ad', 'B', {'in': 0}),
+ ('B', 'Bd', {'out': 0}),
+ ('Bd', 'C', {'in': 0}),
+ ('C', 'Cd', {'out': 0}),
+ ('Cd', 'D', {'in': 0}),
+ ('D', 'Dd', {'out': 0}),
+ ('Dd', 'E', {'in': 0}),
+ ('E', 'Ed', {'out': 0}),
+ ('Ed', 'I', {'in': 0}),
+ ('Cd', 'F', {'in': 0}),
+ ('F', 'Fd', {'out': 0}),
+ ('Fd', 'G', {'in': 0}),
+ ('G', 'Gd', {'out': 0}),
+ ('Gd', 'I', {'in': 1}),
+ ('Cd', 'H', {'in': 0}),
+ ('H', 'Hd', {'out': 0}),
+ ('Hd', 'I', {'in': 2}),
+ ('I', 'Id', {'out': 0}),
+ ('Id', 'J', {'in': 0}),
+ ('J', 'Jd', {'out': 0}),
+ ('Jd', 'K', {'in': 0}),
+ ('K', 'Kd', {'out': 0})],
+
+ [('A', 'Ad', {'out': 0}),
+ ('Ad', 'B', {'in': 0}),
+ ('B', 'Bd', {'out': 0}),
+ ('Bd', 'C', {'in': 0}),
+ ('C', 'Cd', {'out': 0}),
+ ('Cd', 'D', {'in': 0}),
+ ('D', 'Dd', {'out': 0}),
+ ('Dd', 'E', {'in': 0}),
+ ('E', 'Ed', {'out': 0}),
+ ('Ed', 'I', {'in': 0}),
+ ('Cd', 'F', {'in': 0}),
+ ('F', 'Fd', {'out': 0}),
+ ('Fd', 'G', {'in': 0}),
+ ('G', 'Gd', {'out': 0}),
+ ('Gd', 'I', {'in': 1}),
+ ('Cd', 'H', {'in': 0}),
+ ('H', 'Hd', {'out': 0}),
+ ('Hd', 'I', {'in': 2}),
+ ('I', 'Id', {'out': 0}),
+ ('Id', 'J', {'in': 0}),
+ ('J', 'Jd', {'out': 0}),
+ ('Jd', 'K', {'in': 0}),
+ ('K', 'Kd', {'out': 0}),
+ ('Ad', 'E', {'in': 1}),
+ ('Bd', 'K', {'in': 1}),
+ ('Hd', 'J', {'in': 1})],
+
+ [('A', 'Ad', {'out': 0}),
+ ('Ad', 'B', {'in': 0}),
+ ('B', 'Bd', {'out': 0}),
+ ('Bd', 'C', {'in': 0}),
+ ('C', 'Cd', {'out': 0}),
+ ('Cd', 'D', {'in': 0}),
+ ('D', 'Dd', {'out': 0}),
+ ('Dd', 'E', {'in': 0}),
+ ('E', 'Ed', {'out': 0}),
+ ('Ed', 'I', {'in': 0}),
+ ('Cd', 'F', {'in': 0}),
+ ('F', 'Fd', {'out': 0}),
+ ('Fd', 'G', {'in': 0}),
+ ('G', 'Gd', {'out': 0}),
+ ('Gd', 'I', {'in': 1}),
+ ('Cd', 'H', {'in': 0}),
+ ('H', 'Hd', {'out': 0}),
+ ('Hd', 'I', {'in': 2}),
+ ('I', 'Id', {'out': 0}),
+ ('Id', 'J', {'in': 0}),
+ ('J', 'Jd', {'out': 0}),
+ ('Jd', 'K', {'in': 0}),
+ ('K', 'Kd', {'out': 0}),
+ ('Ad', 'E', {'in': 1}),
+ ('Bd', 'K', {'in': 1}),
+ ('Hd', 'J', {'in': 1}),
+ ('Dd', 'F', {'in': 1}),
+ ('Fd', 'H', {'in': 1}),
+ ('Gd', 'H', {'in': 0})]
+ )
+ def test_determined_topological_sort(self, edges):
+ nodes = {'A': {'type': 'Identity', 'kind': 'op'},
+ 'B': {'type': 'Identity', 'kind': 'op'},
+ 'C': {'type': 'Identity', 'kind': 'op'},
+ 'D': {'type': 'Identity', 'kind': 'op'},
+ 'E': {'type': 'Identity', 'kind': 'op'},
+ 'F': {'type': 'Identity', 'kind': 'op'},
+ 'G': {'type': 'Identity', 'kind': 'op'},
+ 'H': {'type': 'Identity', 'kind': 'op'},
+ 'I': {'type': 'Identity', 'kind': 'op'},
+ 'J': {'type': 'Identity', 'kind': 'op'},
+ 'K': {'type': 'Identity', 'kind': 'op'},
+ 'Ad': {'value': None, 'kind': 'data'},
+ 'Bd': {'value': None, 'kind': 'data'},
+ 'Cd': {'value': None, 'kind': 'data'},
+ 'Dd': {'value': None, 'kind': 'data'},
+ 'Ed': {'value': None, 'kind': 'data'},
+ 'Fd': {'value': None, 'kind': 'data'},
+ 'Gd': {'value': None, 'kind': 'data'},
+ 'Hd': {'value': None, 'kind': 'data'},
+ 'Id': {'value': None, 'kind': 'data'},
+ 'Jd': {'value': None, 'kind': 'data'},
+ 'Kd': {'value': None, 'kind': 'data'},
+ }
+
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ outputs = [Node(graph, 'Kd')]
+ for i in range(100):
+ op_order, data_order = determined_sort(outputs)
+ self.assertListEqual(op_order, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'])
+ self.assertListEqual(data_order, ['Ad', 'Bd', 'Cd', 'Dd', 'Ed', 'Fd', 'Gd', 'Hd', 'Id', 'Jd', 'Kd'])
+
+
+class TestGetFWTensorName(unittest.TestCase):
+ def test_get_fw_tensor_debug_info(self):
+ nodes = {
+ 'A': {'type': 'Identity', 'kind': 'op'},
+ 'B': {'type': 'Identity', 'kind': 'op'},
+ 'C': {'type': 'Identity', 'kind': 'op'},
+ 'Ad': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('A', 0)]},
+ 'Bd': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('B', 0)]},
+ 'Cd': {'value': None, 'kind': 'data'},
+ }
+ edges = [
+ ('A', 'Ad', {'out': 0}),
+ ('Ad', 'B', {'in': 0}),
+ ('B', 'Bd', {'out': 0}),
+ ('Bd', 'C', {'in': 0}),
+ ('C', 'Cd', {'out': 0})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ fw_debug_info = get_fw_tensor_debug_info(Node(graph, 'Cd'))
+ self.assertEqual(len(fw_debug_info), 1)
+ self.assertEqual(fw_debug_info[0], ('B', 0))
+
+
+class TestOutputSort(unittest.TestCase):
+ def test_get_sorted_outputs(self):
+ nodes = {'A': {'type': 'Identity', 'kind': 'op'},
+ 'B': {'type': 'Identity', 'kind': 'op'},
+ 'C': {'type': 'Identity', 'kind': 'op'},
+ 'D': {'type': 'Identity', 'kind': 'op'},
+ 'E': {'type': 'Identity', 'kind': 'op'},
+ 'F': {'type': 'Identity', 'kind': 'op'},
+ 'G': {'type': 'Identity', 'kind': 'op'},
+ 'H': {'type': 'Identity', 'kind': 'op'},
+ 'Ad': {'value': None, 'kind': 'data'},
+ 'Bd': {'value': None, 'kind': 'data'},
+ 'Cd': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('C', 0)]},
+ 'Dd': {'value': None, 'kind': 'data'},
+ 'Ed': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('E', 0)]},
+ 'Fd': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('F', 0)]},
+ 'Gd': {'value': None, 'kind': 'data'},
+ 'Hd': {'value': None, 'kind': 'data'}
+ }
+ edges = [
+ ('A', 'Ad', {'out': 0}),
+ ('Ad', 'B', {'in': 0}),
+ ('B', 'Bd', {'out': 0}),
+ ('Bd', 'C', {'in': 0}),
+ ('C', 'Cd', {'out': 0}),
+ ('Cd', 'D', {'in': 0}),
+ ('D', 'Dd', {'out': 0}),
+ ('Dd', 'E', {'in': 0}),
+ ('E', 'Ed', {'out': 0}),
+ ('Cd', 'F', {'in': 0}),
+ ('F', 'Fd', {'out': 0}),
+ ('Fd', 'G', {'in': 0}),
+ ('G', 'Gd', {'out': 0}),
+ ('Cd', 'H', {'in': 0}),
+ ('H', 'Hd', {'out': 0})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ self.assertListEqual([node.id for node in get_sorted_outputs(graph)], ['Hd', 'Ed', 'Gd'])
+
+ def test_get_sorted_outputs_fine_situation(self):
+ nodes = {'A': {'type': 'Identity', 'kind': 'op'},
+ 'B': {'type': 'Identity', 'kind': 'op'},
+ 'C': {'type': 'Identity', 'kind': 'op'},
+ 'D': {'type': 'Identity', 'kind': 'op'},
+ 'E': {'type': 'Identity', 'kind': 'op'},
+ 'F': {'type': 'Identity', 'kind': 'op'},
+ 'G': {'type': 'Identity', 'kind': 'op'},
+ 'H': {'type': 'Identity', 'kind': 'op'},
+ 'Ad': {'value': None, 'kind': 'data'},
+ 'Bd': {'value': None, 'kind': 'data'},
+ 'Cd': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('C', 0)]},
+ 'Dd': {'value': None, 'kind': 'data'},
+ 'Ed': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('E', 0)]},
+ 'Fd': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('F', 0)]},
+ 'Gd': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('G', 0)]},
+ 'Hd': {'value': None, 'kind': 'data', 'fw_tensor_debug_info': [('H', 0)]}
+ }
+ edges = [
+ ('A', 'Ad', {'out': 0}),
+ ('Ad', 'B', {'in': 0}),
+ ('B', 'Bd', {'out': 0}),
+ ('Bd', 'C', {'in': 0}),
+ ('C', 'Cd', {'out': 0}),
+ ('Cd', 'D', {'in': 0}),
+ ('D', 'Dd', {'out': 0}),
+ ('Dd', 'E', {'in': 0}),
+ ('E', 'Ed', {'out': 0}),
+ ('Cd', 'F', {'in': 0}),
+ ('F', 'Fd', {'out': 0}),
+ ('Fd', 'G', {'in': 0}),
+ ('G', 'Gd', {'out': 0}),
+ ('Cd', 'H', {'in': 0}),
+ ('H', 'Hd', {'out': 0})
+ ]
+ graph = build_graph_with_edge_attrs(nodes, edges)
+ self.assertListEqual([node.id for node in get_sorted_outputs(graph)], ['Ed', 'Gd', 'Hd'])
diff --git a/model-optimizer/mo/pipeline/kaldi_test.py b/model-optimizer/mo/pipeline/kaldi_test.py
new file mode 100644
index 000000000..2fe183330
--- /dev/null
+++ b/model-optimizer/mo/pipeline/kaldi_test.py
@@ -0,0 +1,101 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.pipeline.kaldi import apply_biases_to_last_layer
+from mo.utils.unittest.graph import build_graph
+
+
+class TestKaldiPipeline(unittest.TestCase):
+ def test_apply_biases_to_ScaleShift(self):
+ nodes = {'input': {'kind': 'data'},
+ 'weights': {'value': None, 'kind': 'data'},
+ 'biases': {'value': np.zeros(10), 'kind': 'data'},
+ 'sc': {'op': 'ScaleShift', 'kind': 'op'},
+ 'output': {'kind': 'data'}
+ }
+ graph = build_graph(nodes,
+ [
+ ('input', 'sc'),
+ ('weights', 'sc'),
+ ('biases', 'sc'),
+ ('sc', 'output')
+ ],
+ {
+ 'output': {
+ 'is_output': True
+ }
+ })
+ counts = -0.5 * np.ones(10)
+ apply_biases_to_last_layer(graph, counts)
+ sc_node = Node(graph, 'sc')
+ self.assertTrue(np.array_equal(sc_node.in_node(2).value, -counts))
+
+ def test_apply_biases_to_FullyConnected(self):
+ nodes = {'input': {'kind': 'data'},
+ 'weights': {'kind': 'data'},
+ 'biases': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc': {'op': 'FullyConnected', 'kind': 'op'},
+ 'output': {'kind': 'data'}
+ }
+ graph = build_graph(nodes,
+ [
+ ('input', 'fc'),
+ ('weights', 'fc'),
+ ('biases', 'fc'),
+ ('fc', 'output')
+ ],
+ {
+ 'output': {
+ 'is_output': True
+ }
+ })
+ counts = -0.5 * np.ones(10)
+ apply_biases_to_last_layer(graph, counts)
+ fc_node = Node(graph, 'fc')
+ self.assertTrue(np.array_equal(fc_node.in_node(2).value, -counts))
+
+ def test_apply_biases_to_graph_with_SoftMax(self):
+ nodes = {'input': {'kind': 'data'},
+ 'weights': {'value': None, 'kind': 'data'},
+ 'biases': {'value': None, 'shape': None, 'kind': 'data'},
+ 'fc': {'op': 'FullyConnected', 'kind': 'op'},
+ 'data': {'kind': 'data'},
+ 'softmax': {'op': 'SoftMax', 'kind': 'op'},
+ 'output': {'kind': 'data'}
+ }
+ graph = build_graph(nodes,
+ [
+ ('input', 'fc'),
+ ('weights', 'fc'),
+ ('biases', 'fc'),
+ ('fc', 'data'),
+ ('data', 'softmax'),
+ ('softmax', 'output')
+ ],
+ {
+ 'output': {
+ 'is_output': True
+ }
+ })
+ counts = -0.5 * np.ones(10)
+ apply_biases_to_last_layer(graph, counts)
+ fc_node = Node(graph, 'fc')
+ self.assertTrue(np.array_equal(fc_node.in_node(2).value, -counts))
diff --git a/model-optimizer/mo/pipeline/mx_test.py b/model-optimizer/mo/pipeline/mx_test.py
new file mode 100644
index 000000000..66e49bb32
--- /dev/null
+++ b/model-optimizer/mo/pipeline/mx_test.py
@@ -0,0 +1,67 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.pipeline.mx import add_input_data_to_prior_boxes
+from mo.utils.unittest.graph import build_graph
+
+
+class TestMxnetPipeline(unittest.TestCase):
+ def test_mxnet_pipeline_1(self):
+ graph = build_graph(
+ {'data': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_multi_box': {'type': '_contrib_MultiBoxPrior', 'kind': 'op', 'op': '_contrib_MultiBoxPrior'},
+ },
+ [('data', 'node_2'),
+ ('node_2', 'node_multi_box')],
+ {
+ 'data': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 10, 10])},
+ })
+
+ add_input_data_to_prior_boxes(graph)
+ node_multi_box = Node(graph, 'node_multi_box')
+
+ node_input1 = node_multi_box.in_node(0)
+ node_input2 = node_multi_box.in_node(1)
+ self.assertEqual(node_input1.name, 'node_2')
+ self.assertEqual(node_input2.name, 'data')
+
+ def test_mxnet_pipeline_2(self):
+ graph = build_graph(
+ {'node_1': {'type': 'Identity', 'value': None, 'kind': 'op', 'op': 'Placeholder'},
+ 'node_2': {'type': 'Identity', 'value': None, 'kind': 'op'},
+ 'node_multi_box': {'type': '_contrib_MultiBoxPrior', 'kind': 'op', 'op': '_contrib_MultiBoxPrior'},
+ },
+ [('node_1', 'node_2'),
+ ('node_2', 'node_multi_box')],
+ {
+ 'node_1': {'shape': np.array([1, 3, 227, 227])},
+ 'node_2': {'shape': np.array([1, 3, 10, 10])},
+ })
+
+ add_input_data_to_prior_boxes(graph, 'node_1')
+ node_multi_box = Node(graph, 'node_multi_box')
+
+ node_input1 = node_multi_box.in_node(0)
+ node_input2 = node_multi_box.in_node(1)
+ self.assertEqual(node_input1.name, 'node_2')
+ self.assertEqual(node_input2.name, 'node_1')
diff --git a/model-optimizer/mo/utils/cli_parser_test.py b/model-optimizer/mo/utils/cli_parser_test.py
new file mode 100644
index 000000000..1646273f7
--- /dev/null
+++ b/model-optimizer/mo/utils/cli_parser_test.py
@@ -0,0 +1,672 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import argparse
+import imp
+import os
+import tempfile
+import unittest
+from unittest.mock import patch
+
+import numpy as np
+
+from mo.utils.cli_parser import get_placeholder_shapes, get_tuple_values, get_mean_scale_dictionary, get_model_name, \
+ get_absolute_path, parse_tuple_pairs, check_positive, writable_dir, readable_dirs, \
+ readable_file
+from mo.utils.error import Error
+
+
+class TestingMeanScaleGetter(unittest.TestCase):
+ def test_tuple_parser(self):
+ tuple_values = "data(1.1,22.22,333.333),info[2.2,33.33,444.444]"
+ result = parse_tuple_pairs(tuple_values)
+ exp_res = {
+ 'data': np.array([1.1, 22.22, 333.333]),
+ 'info': np.array([2.2, 33.33, 444.444])
+ }
+ for el in exp_res.keys():
+ np.array_equal(result[el], exp_res[el])
+
+ def test_tuple_parser_same_values(self):
+ tuple_values = "data(1.1,22.22,333.333),info[1.1,22.22,333.333]"
+ result = parse_tuple_pairs(tuple_values)
+ exp_res = {
+ 'data': np.array([1.1, 22.22, 333.333]),
+ 'info': np.array([1.1, 22.22, 333.333])
+ }
+ for el in exp_res.keys():
+ np.array_equal(result[el], exp_res[el])
+
+ def test_tuple_parser_no_inputs(self):
+ tuple_values = "(1.1,22.22,333.333),[2.2,33.33,444.444]"
+ result = parse_tuple_pairs(tuple_values)
+ exp_res = [np.array([1.1, 22.22, 333.333]),
+ np.array([2.2, 33.33, 444.444])]
+ for i in range(0, len(exp_res)):
+ np.array_equal(result[i], exp_res[i])
+
+ def test_tuple_parser_error(self):
+ tuple_values = "(1.1,22.22,333.333),data[2.2,33.33,444.444]"
+ self.assertRaises(Error, parse_tuple_pairs, tuple_values)
+
+ def test_mean_scale_no_input(self):
+ mean_values = "data(1.1,22.22,333.333)"
+ scale_values = "info[1.1,22.22,333.333]"
+ result = get_mean_scale_dictionary(parse_tuple_pairs(mean_values), parse_tuple_pairs(scale_values), None)
+ exp_res = {
+ 'data': {
+ 'mean': np.array([1.1, 22.22, 333.333]),
+ 'scale': None
+ },
+ 'info': {
+ 'mean': None,
+ 'scale': np.array([1.1, 22.22, 333.333])
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_mean_scale_no_input_diff_len(self):
+ mean_values = "data(1.1,22.22,333.333),info(2.1,33.22,333.333)"
+ scale_values = "info[1.1,22.22,333.333]"
+ result = get_mean_scale_dictionary(parse_tuple_pairs(mean_values), parse_tuple_pairs(scale_values), None)
+ exp_res = {
+ 'data': {
+ 'mean': np.array([1.1, 22.22, 333.333]),
+ 'scale': None
+ },
+ 'info': {
+ 'mean': np.array([2.1, 33.22, 333.333]),
+ 'scale': np.array([1.1, 22.22, 333.333])
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_mean_only_input(self):
+ mean_values = "data(1.1,22.22,333.333)"
+ result = get_mean_scale_dictionary(parse_tuple_pairs(mean_values), parse_tuple_pairs(''), None)
+ exp_res = {
+ 'data': {
+ 'mean': np.array([1.1, 22.22, 333.333]),
+ 'scale': None
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_scale_only_input(self):
+ scale_values = "data(1.1,22.22,333.333)"
+ result = get_mean_scale_dictionary(parse_tuple_pairs(''), parse_tuple_pairs(scale_values), None)
+ exp_res = {
+ 'data': {
+ 'mean': None,
+ 'scale': np.array([1.1, 22.22, 333.333])
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_scale_only_no_input(self):
+ scale_values = "(1.1,22.22,333.333)"
+ mean_values = ""
+ mean = parse_tuple_pairs(mean_values)
+ scale = parse_tuple_pairs(scale_values)
+ result = get_mean_scale_dictionary(mean, scale, None)
+ exp_res = [
+ [
+ None,
+ np.array([1.1, 22.22, 333.333])
+ ]
+ ]
+ for i in range(len(exp_res)):
+ for j in range(len(exp_res[i])):
+ if type(exp_res[i][j]) is np.ndarray:
+ np.array_equal(exp_res[i][j], result[i][j])
+ else:
+ self.assertEqual(exp_res[i][j], result[i][j])
+
+ def test_scale_only_with_input(self):
+ scale_values = "(1.1,22.22,333.333)"
+ mean_values = ""
+ mean = parse_tuple_pairs(mean_values)
+ scale = parse_tuple_pairs(scale_values)
+ result = get_mean_scale_dictionary(mean, scale, 'data')
+ exp_res = {
+ 'data': {
+ 'mean': None,
+ 'scale': np.array([1.1, 22.22, 333.333])
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_2_scale_only_with_input(self):
+ scale_values = "(1.1,22.22,333.333),(1.2,22.33,333.444)"
+ mean_values = ""
+ mean = parse_tuple_pairs(mean_values)
+ scale = parse_tuple_pairs(scale_values)
+ result = get_mean_scale_dictionary(mean, scale, 'data,info')
+ exp_res = {
+ 'data': {
+ 'mean': None,
+ 'scale': np.array([1.1, 22.22, 333.333])
+ },
+ 'info': {
+ 'mean': None,
+ 'scale': np.array([1.2, 22.33, 333.444])
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_2_mean_only_with_input(self):
+ scale_values = ""
+ mean_values = "(1.1,22.22,333.333),(1.2,22.33,333.444)"
+ mean = parse_tuple_pairs(mean_values)
+ scale = parse_tuple_pairs(scale_values)
+ result = get_mean_scale_dictionary(mean, scale, 'data,info')
+ exp_res = {
+ 'data': {
+ 'mean': np.array([1.1, 22.22, 333.333]),
+ 'scale': None,
+ },
+ 'info': {
+ 'mean': np.array([1.2, 22.33, 333.444]),
+ 'scale': None,
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_mean_only_with_input(self):
+ scale_values = ""
+ mean_values = "(1.1,22.22,333.333)"
+ mean = parse_tuple_pairs(mean_values)
+ scale = parse_tuple_pairs(scale_values)
+ result = get_mean_scale_dictionary(mean, scale, 'data')
+ exp_res = {
+ 'data': {
+ 'mean': np.array([1.1, 22.22, 333.333]),
+ 'scale': None
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_mean_scale_diff_no_input(self):
+ scale_values = "(1.1,22.22,333.333),(1.1,22.22,333.333)"
+ mean_values = "(2.1,11.22,444.333)"
+ mean = parse_tuple_pairs(mean_values)
+ scale = parse_tuple_pairs(scale_values)
+ result = get_mean_scale_dictionary(mean, scale, None)
+ exp_res = [
+ [
+ np.array([2.1, 11.22, 444.333]), # mean
+ np.array([1.1, 22.22, 333.333]) # scale
+ ],
+ [
+ None, # mean
+ np.array([1.1, 22.22, 333.333]) # scale
+ ]
+ ]
+ for i in range(len(exp_res)):
+ for j in range(len(exp_res[i])):
+ if type(exp_res[i][j]) is np.ndarray:
+ np.array_equal(exp_res[i][j], result[i][j])
+ else:
+ self.assertEqual(exp_res[i][j], result[i][j])
+
+ def test_multi_mean_scale_no_input(self):
+ mean_values = "data(1.1,22.22,333.333),info(2.1,33.22,444.333)"
+ scale_values = "data[1.1,22.22,333.333],info[2.1,33.22,444.333]"
+ result = get_mean_scale_dictionary(parse_tuple_pairs(mean_values), parse_tuple_pairs(scale_values), None)
+ exp_res = {
+ 'data': {
+ 'mean': np.array([1.1, 22.22, 333.333]),
+ 'scale': np.array([1.1, 22.22, 333.333])
+ },
+ 'info': {
+ 'mean': np.array([2.1, 33.22, 444.333]),
+ 'scale': np.array([2.1, 33.22, 444.333])
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_multi_mean_scale_input(self):
+ mean_values = "data(1.1,22.22,333.333),info(2.1,33.22,444.333)"
+ scale_values = "data[1.1,22.22,333.333],info[2.1,33.22,444.333]"
+ input_names = 'data,info'
+ result = get_mean_scale_dictionary(parse_tuple_pairs(mean_values), parse_tuple_pairs(scale_values), input_names)
+ exp_res = {
+ 'data': {
+ 'mean': np.array([1.1, 22.22, 333.333]),
+ 'scale': np.array([1.1, 22.22, 333.333])
+ },
+ 'info': {
+ 'mean': np.array([2.1, 33.22, 444.333]),
+ 'scale': np.array([2.1, 33.22, 444.333])
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_multi_mean_scale_input_arrays(self):
+ mean_values = "(1.1,22.22,333.333),(2.1,33.22,444.333)"
+ scale_values = "[1.1,22.22,333.333],[2.1,33.22,444.333]"
+ input_names = 'data,info'
+ result = get_mean_scale_dictionary(parse_tuple_pairs(mean_values), parse_tuple_pairs(scale_values), input_names)
+ exp_res = {
+ 'data': {
+ 'mean': np.array([1.1, 22.22, 333.333]),
+ 'scale': np.array([1.1, 22.22, 333.333])
+ },
+ 'info': {
+ 'mean': np.array([2.1, 33.22, 444.333]),
+ 'scale': np.array([2.1, 33.22, 444.333])
+ }
+ }
+ for input in exp_res.keys():
+ for key in exp_res[input].keys():
+ if type(exp_res[input][key]) is np.ndarray:
+ np.array_equal(exp_res[input][key], result[input][key])
+ else:
+ self.assertEqual(exp_res[input][key], result[input][key])
+
+ def test_multi_mean_scale_arrays_no_input(self):
+ mean_values = "(1.1,22.22,333.333),(2.1,33.22,444.333)"
+ scale_values = "[1.1,22.22,333.333],[2.1,33.22,444.333]"
+ result = get_mean_scale_dictionary(parse_tuple_pairs(mean_values), parse_tuple_pairs(scale_values), None)
+ exp_res = [
+ [
+ np.array([1.1, 22.22, 333.333]), # mean
+ np.array([1.1, 22.22, 333.333]) # scale
+ ],
+ [
+ np.array([2.1, 33.22, 444.333]), # mean
+ np.array([2.1, 33.22, 444.333]) # scale
+ ]
+ ]
+ for i in range(0, len(exp_res)):
+ for j in range(0, len(exp_res[i])):
+ np.array_equal(exp_res[i][j], result[i][j])
+
+
+class TestSingleTupleParsing(unittest.TestCase):
+ def test_get_values_ideal(self):
+ values = "(1.11, 22.22, 333.333)"
+ result = get_tuple_values(values)
+ exp_res = ['1.11, 22.22, 333.333']
+ self.assertEqual(exp_res, result)
+
+ def test_get_values_ideal_spaces(self):
+ values = "(1 , 22 ,333)"
+ result = get_tuple_values(values)
+ exp_res = ['1 , 22 ,333']
+ self.assertEqual(exp_res, result)
+
+ def test_get_values_ideal_square(self):
+ values = "[1,22,333]"
+ result = get_tuple_values(values)
+ exp_res = ['1,22,333']
+ self.assertEqual(exp_res, result)
+
+ def test_get_values_ideal_square_spaces(self):
+ values = "[1 , 22 ,333]"
+ result = get_tuple_values(values)
+ exp_res = ['1 , 22 ,333']
+ self.assertEqual(exp_res, result)
+
+ def test_get_neg_values_ideal(self):
+ values = "(-1,-22,-333)"
+ result = get_tuple_values(values)
+ exp_res = ['-1,-22,-333']
+ self.assertEqual(exp_res, result)
+
+ def test_get_neg_values_minus(self):
+ values = "(-1,--22,-3-33)"
+ self.assertRaises(Error, get_tuple_values, values)
+
+ def test_get_values_unbalanced(self):
+ values = "(1,22,333]"
+ self.assertRaises(Error, get_tuple_values, values)
+
+ def test_get_values_unbalanced2(self):
+ values = "[1,22,333)"
+ self.assertRaises(Error, get_tuple_values, values)
+
+ def test_get_values_exactly_3(self):
+ values = "[1,22,333,22]"
+ self.assertRaises(Error, get_tuple_values, values)
+
+ def test_get_values_exactly_3_1(self):
+ values = "[1,22]"
+ self.assertRaises(Error, get_tuple_values, values)
+
+ def test_get_values_empty(self):
+ values = ""
+ self.assertRaises(Error, get_tuple_values, values)
+
+ def test_get_values_empty_tuple(self):
+ values = ()
+ result = get_tuple_values(values)
+ exp_res = ()
+ self.assertEqual(exp_res, result)
+
+
+class TestShapesParsing(unittest.TestCase):
+ def test_get_shapes_several_inputs_several_shapes(self):
+ argv_input = "inp1,inp2"
+ input_shapes = "(1,22,333,123), (-1,45,7,1)"
+ result = get_placeholder_shapes(argv_input, input_shapes)
+ exp_res = {'inp1': np.array([1, 22, 333, 123]), 'inp2': np.array([-1, 45, 7, 1])}
+ self.assertEqual(list(exp_res.keys()), list(result.keys()))
+ for i in exp_res.keys():
+ np.testing.assert_array_equal(result[i], exp_res[i])
+
+ def test_get_shapes_several_inputs_several_shapes_not_equal(self):
+ argv_input = "inp1,inp2,inp3"
+ input_shapes = "(1,22,333,123), (-1,45,7,1)"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_several_shapes_one_input(self):
+ argv_input = "inp1"
+ input_shapes = "(1,22,333,123), (-1,45,7,1), (-1,456,7,1)"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_several_shapes_no_input(self):
+ argv_input = ""
+ input_shapes = "(1,22,333,123), (-1,45,7,1), (-1,456,7,1)"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_one_shape(self):
+ argv_input = "inp1"
+ input_shapes = "(1,22,333,123)"
+ result = get_placeholder_shapes(argv_input, input_shapes)
+ exp_res = {'inp1': np.array([1, 22, 333, 123])}
+ self.assertEqual(list(exp_res.keys()), list(result.keys()))
+ for i in exp_res.keys():
+ np.testing.assert_array_equal(result[i], exp_res[i])
+
+ def test_get_shapes_no_input_no_shape(self):
+ argv_input = ""
+ input_shapes = ""
+ result = get_placeholder_shapes(argv_input, input_shapes)
+ exp_res = np.array([None])
+ np.testing.assert_array_equal(result, exp_res)
+
+ def test_get_shapes_no_input_one_shape(self):
+ argv_input = ""
+ input_shapes = "(12,4,1)"
+ result = get_placeholder_shapes(argv_input, input_shapes)
+ exp_res = np.array([12, 4, 1])
+ np.testing.assert_array_equal(result, exp_res)
+
+ def test_get_shapes_no_input_one_shape2(self):
+ argv_input = ""
+ input_shapes = "[12,4,1]"
+ result = get_placeholder_shapes(argv_input, input_shapes)
+ exp_res = np.array([12, 4, 1])
+ np.testing.assert_array_equal(result, exp_res)
+
+ def test_get_shapes_no_input_two_shapes(self):
+ argv_input = ""
+ input_shapes = "(12,4,1),(5,4,3)"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_no_shape(self):
+ argv_input = "inp1"
+ input_shapes = ""
+ result = get_placeholder_shapes(argv_input, input_shapes)
+ exp_res = {'inp1': np.array([None])}
+ self.assertEqual(list(exp_res.keys()), list(result.keys()))
+ for i in exp_res.keys():
+ np.testing.assert_array_equal(result[i], exp_res[i])
+
+ def test_get_shapes_one_input_wrong_shape8(self):
+ argv_input = "inp1"
+ input_shapes = "[2,4,1)"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_wrong_shape9(self):
+ argv_input = "inp1"
+ input_shapes = "(2,4,1]"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_wrong_shape10(self):
+ argv_input = "inp1"
+ input_shapes = "(2,,,4,1]"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_wrong_shape2(self):
+ argv_input = "inp1"
+ input_shapes = "(2,4,1"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_wrong_shape3(self):
+ argv_input = "inp1"
+ input_shapes = "2,4,1"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_wrong_shape4(self):
+ argv_input = "inp1"
+ input_shapes = "2;4;1"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_wrong_shape5(self):
+ argv_input = "inp1"
+ input_shapes = "2, 4,1"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_wrong_shape6(self):
+ argv_input = "inp1"
+ input_shapes = "(2, 4,1),[4,6,8]"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_wrong_shape7(self):
+ argv_input = "inp1"
+ input_shapes = "[2,4,1],(4,6,8)"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_several_shapes(self):
+ argv_input = "inp1"
+ input_shapes = "(2,4,1),(4,6,8)"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_first_neg_shape1(self):
+ argv_input = "inp1,inp2"
+ input_shapes = "(-1,4,1),(4,6,8)"
+ result = get_placeholder_shapes(argv_input, input_shapes)
+ exp_res = {'inp1': np.array([-1, 4, 1]), 'inp2': np.array([4, 6, 8])}
+ self.assertEqual(list(exp_res.keys()), list(result.keys()))
+ for i in exp_res.keys():
+ np.testing.assert_array_equal(result[i], exp_res[i])
+
+ def test_get_shapes_one_input_first_neg_shape_not_one(self):
+ argv_input = "inp1"
+ input_shapes = "(-12,4,1),(4,6,8)"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+ def test_get_shapes_one_input_any_neg_shape(self):
+ argv_input = "inp1, inp2"
+ input_shapes = "(12,4,1),(4,-6,8)"
+ self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
+
+
+class TestModelNameParsing(unittest.TestCase):
+ def test_model_name_ideal(self):
+ model_name = '/home/models/mymodel.caffemodel'
+ res = get_model_name(model_name)
+ exp_res = 'mymodel'
+ self.assertEqual(exp_res, res)
+
+ def test_model_name_no_name(self):
+ model_name = '/home/models/.caffemodel'
+ res = get_model_name(model_name)
+ exp_res = 'model'
+ self.assertEqual(exp_res, res)
+
+ def test_model_name_no_ext(self):
+ model_name = '/home/models/caffemodel'
+ res = get_model_name(model_name)
+ exp_res = 'caffemodel'
+ self.assertEqual(exp_res, res)
+
+ def test_model_name_no_name_no_path(self):
+ model_name = '.caffemodel'
+ res = get_model_name(model_name)
+ exp_res = 'model'
+ self.assertEqual(exp_res, res)
+
+ @patch("mo.utils.cli_parser.os")
+ def test_model_name_win(self, old_os):
+ old_os.path.basename.return_value = "caffemodel"
+ old_os.path.splitext.return_value = ("caffemodel", "")
+ model_name = r'\home\models\caffemodel'
+ res = get_model_name(model_name)
+
+ exp_res = 'caffemodel'
+ self.assertEqual(exp_res, res)
+
+ def test_model_name_dots(self):
+ model_name = r'/home/models/squeezenet_v1.1.caffemodel'
+ res = get_model_name(model_name)
+ exp_res = 'squeezenet_v1.1'
+ self.assertEqual(exp_res, res)
+
+
+class PositiveChecker(unittest.TestCase):
+ def test_positive_checker_batch(self):
+ res = check_positive('1')
+ self.assertEqual(res, 1)
+
+ def test_positive_checker_batch_negative(self):
+ self.assertRaises(argparse.ArgumentTypeError, check_positive, '-1')
+
+ def test_positive_checker_batch_not_int(self):
+ self.assertRaises(argparse.ArgumentTypeError, check_positive, 'qwe')
+
+
+class PathCheckerFunctions(unittest.TestCase):
+ READABLE_DIR = tempfile.gettempdir()
+ WRITABLE_DIR = os.path.join(tempfile.gettempdir(), 'writable_dir')
+ WRITABLE_NON_EXISTING_DIR = os.path.join(WRITABLE_DIR, 'non_existing_dir')
+ NOT_WRITABLE_DIR = os.path.join(tempfile.gettempdir(), 'not_writable_dir')
+ NOT_WRITABLE_SUB_DIR = os.path.join(tempfile.gettempdir(), 'another_not_writable_dir', 'not_existing_dir')
+ EXISTING_FILE = tempfile.NamedTemporaryFile(mode='r+', delete=False).name
+ NOT_EXISTING_FILE = '/abcd/efgh/ijkl'
+
+ @classmethod
+ def setUpClass(cls):
+ if not os.path.exists(__class__.WRITABLE_DIR):
+ os.makedirs(__class__.WRITABLE_DIR)
+ if os.path.exists(__class__.WRITABLE_NON_EXISTING_DIR):
+ os.removedirs(__class__.WRITABLE_NON_EXISTING_DIR)
+
+ if not os.path.exists(__class__.NOT_WRITABLE_DIR):
+ os.makedirs(__class__.NOT_WRITABLE_DIR)
+ os.chmod(__class__.NOT_WRITABLE_DIR, 0)
+
+ if not os.path.exists(os.path.dirname(__class__.NOT_WRITABLE_SUB_DIR)):
+ os.makedirs(os.path.dirname(__class__.NOT_WRITABLE_SUB_DIR))
+ os.chmod(os.path.dirname(__class__.NOT_WRITABLE_SUB_DIR), 0)
+ if os.path.exists(__class__.NOT_EXISTING_FILE):
+ os.remove(__class__.NOT_EXISTING_FILE)
+
+ @classmethod
+ def tearDownClass(cls):
+ if os.path.exists(__class__.WRITABLE_DIR):
+ os.removedirs(__class__.WRITABLE_DIR)
+ if os.path.exists(__class__.NOT_WRITABLE_DIR):
+ os.removedirs(__class__.NOT_WRITABLE_DIR)
+ if os.path.exists(os.path.dirname(__class__.NOT_WRITABLE_SUB_DIR)):
+ os.removedirs(os.path.dirname(__class__.NOT_WRITABLE_SUB_DIR))
+ if os.path.exists(__class__.EXISTING_FILE):
+ os.remove(__class__.EXISTING_FILE)
+
+ def test_single_writable_dir(self):
+ self.assertEqual(__class__.WRITABLE_DIR, writable_dir(__class__.WRITABLE_DIR))
+
+ def test_single_non_writable_dir(self):
+ with self.assertRaises(Error) as cm:
+ writable_dir(__class__.NOT_WRITABLE_DIR)
+
+ def test_single_non_writable_sub_dir(self):
+ with self.assertRaises(Error) as cm:
+ writable_dir(__class__.NOT_WRITABLE_SUB_DIR)
+
+ def test_multiple_writable_dirs(self):
+ dirs_str = ','.join([__class__.WRITABLE_DIR, __class__.WRITABLE_NON_EXISTING_DIR])
+ self.assertEqual(dirs_str, writable_dir(dirs_str))
+
+ def test_single_writable_non_existing_dir(self):
+ self.assertEqual(__class__.WRITABLE_NON_EXISTING_DIR, writable_dir(__class__.WRITABLE_NON_EXISTING_DIR))
+
+ def test_readable_dirs(self):
+ dirs_str = ','.join([__class__.WRITABLE_DIR, __class__.READABLE_DIR])
+ self.assertEqual(dirs_str, readable_dirs(dirs_str))
+
+ def test_not_readable_dirs(self):
+ dirs_str = ','.join([__class__.WRITABLE_DIR, __class__.WRITABLE_NON_EXISTING_DIR])
+ with self.assertRaises(Error) as cm:
+ readable_dirs(dirs_str)
+
+ def test_readable_file(self):
+ self.assertEqual(__class__.EXISTING_FILE, readable_file(__class__.EXISTING_FILE))
+
+ def test_non_readable_file(self):
+ with self.assertRaises(Error) as cm:
+ readable_file(__class__.NOT_EXISTING_FILE)
diff --git a/model-optimizer/mo/utils/convert.py b/model-optimizer/mo/utils/convert.py
new file mode 100644
index 000000000..edec06fee
--- /dev/null
+++ b/model-optimizer/mo/utils/convert.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+import argparse
+import os
+import sys
+
+import tensorflow as tf
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
+from mo.front.tf.loader import load_tf_graph_def
+
+os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
+
+
+def argparser():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--to_pbtxt", dest='pb', type=str, help="Path to TensorFlow binary model")
+ parser.add_argument('--to_pb', dest='pbtxt', type=str, help="Path to TensorFlow text model")
+ return parser.parse_args()
+
+
+def convert(filename: str, is_text: bool):
+ if not os.path.isfile(filename):
+ raise FileNotFoundError("File doesn't exist: {}".format(filename))
+ new_ext = ".pbtxt" if is_text else ".pb"
+ head, tail = os.path.split(os.path.abspath(filename))
+ print("Convert: {} \n to: {}".format(filename, os.path.join(head, tail + new_ext)))
+ graph_def, _ = load_tf_graph_def(graph_file_name=filename, is_binary=is_text)
+ tf.import_graph_def(graph_def, name='')
+ tf.train.write_graph(graph_def, head, tail + new_ext, as_text=is_text)
+
+
+if __name__ == '__main__':
+ argv = argparser()
+ if argv.pb is None and argv.pbtxt is None:
+ print("Please provide model to convert --to_pb or --to_pbtxt")
+ sys.exit(1)
+ if argv.pb is not None:
+ convert(argv.pb, True)
+ if argv.pbtxt is not None:
+ convert(argv.pbtxt, False)
diff --git a/model-optimizer/mo/utils/graph_test.py b/model-optimizer/mo/utils/graph_test.py
new file mode 100644
index 000000000..5d4ed57db
--- /dev/null
+++ b/model-optimizer/mo/utils/graph_test.py
@@ -0,0 +1,214 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+import networkx as nx
+
+from mo.utils.error import Error
+from mo.utils.graph import dfs, bfs_search, is_connected_component, sub_graph_between_nodes
+
+
+class TestGraphUtils(unittest.TestCase):
+ def test_simple_dfs(self):
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 5)))
+ graph.add_edges_from([(1, 2), (1, 3), (3, 4)])
+
+ visited = set()
+ order = dfs(graph, 1, visited)
+ self.assertTrue(order == [4, 3, 2, 1] or order == [2, 4, 3, 1])
+
+ def test_bfs_search_default_start_nodes(self):
+ """
+ Check that BFS automatically determines input nodes and start searching from them.
+ """
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 6)))
+ graph.add_edges_from([(1, 3), (2, 3), (3, 4), (4, 5)])
+
+ order = bfs_search(graph)
+ self.assertTrue(order == [1, 2, 3, 4, 5] or order == [2, 1, 3, 4, 5])
+
+ def test_bfs_search_specific_start_nodes(self):
+ """
+ Check that BFS stars from the user defined nodes and doesn't go in backward edge direction.
+ """
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 7)))
+ graph.add_edges_from([(1, 3), (2, 3), (3, 4), (4, 5), (6, 1)])
+
+ order = bfs_search(graph, [1])
+ self.assertTrue(order == [1, 3, 4, 5])
+
+ def test_is_connected_component_two_separate_sub_graphs(self):
+ """
+ Check that if there are two separate sub-graphs the function returns False.
+ """
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 7)))
+ graph.add_edges_from([(1, 2), (2, 3), (4, 5), (5, 6)])
+ self.assertFalse(is_connected_component(graph, list(range(1, 7))))
+ self.assertFalse(is_connected_component(graph, [1, 3]))
+ self.assertFalse(is_connected_component(graph, [6, 4]))
+ self.assertFalse(is_connected_component(graph, [2, 5]))
+
+ def test_is_connected_component_two_separate_sub_graphs_divided_by_ignored_node(self):
+ """
+ Check that if there are two separate sub-graphs the function connected by an edge going through the ignored node
+ then the function returns False.
+ """
+ graph = nx.MultiDiGraph()
+ node_names = list(range(1, 8))
+ graph.add_nodes_from(node_names)
+ graph.add_edges_from([(1, 2), (2, 3), (4, 5), (5, 6), (1, 7), (7, 4)])
+ self.assertFalse(is_connected_component(graph, list(range(1, 7))))
+
+ def test_is_connected_component_connected(self):
+ """
+ Check that if the sub-graph is connected.
+ """
+ graph = nx.MultiDiGraph()
+ node_names = list(range(1, 8))
+ graph.add_nodes_from(node_names)
+ graph.add_edges_from([(1, 2), (2, 3), (4, 5), (5, 6), (1, 7), (7, 4)])
+ self.assertTrue(is_connected_component(graph, list(range(1, 8))))
+
+ def test_is_connected_component_edges_direction_is_ignored(self):
+ """
+ Check that edges direction is ignored when checking for the connectivity.
+ """
+ graph = nx.MultiDiGraph()
+ node_names = list(range(1, 5))
+ graph.add_nodes_from(node_names)
+ graph.add_edges_from([(2, 1), (2, 3), (4, 3)])
+ self.assertTrue(is_connected_component(graph, node_names))
+ self.assertTrue(is_connected_component(graph, [2, 1]))
+ self.assertTrue(is_connected_component(graph, [4, 2, 3]))
+
+ def test_is_connected_component_edges_direction_is_ignored_not_connected(self):
+ """
+ Check that edges direction is ignored when checking for the connectivity. In this case the graph is not
+ connected.
+ """
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 5)))
+ graph.add_edges_from([(2, 1), (2, 3), (4, 3)])
+ self.assertFalse(is_connected_component(graph, [1, 2, 4]))
+ self.assertFalse(is_connected_component(graph, [1, 4]))
+ self.assertFalse(is_connected_component(graph, [2, 4]))
+ self.assertFalse(is_connected_component(graph, [3, 4, 1]))
+
+ def test_sub_graph_between_nodes_include_incoming_edges_for_internal_nodes(self):
+ """
+ Check that the function adds input nodes for the internal nodes of the graph. For example, we need to add node 5
+ and 6 in the case below if we find match from node 1 till node 4.
+ 6 -> 5 ->
+ \
+ 1 -> 2 -> 3 -> 4
+ :return:
+ """
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 7)))
+ graph.add_edges_from([(1, 2), (2, 3), (3, 4), (5, 2), (6, 5)])
+ sub_graph_nodes = sub_graph_between_nodes(graph, [1], [4])
+ self.assertIsNotNone(sub_graph_nodes)
+ self.assertListEqual(sorted(sub_graph_nodes), list(range(1, 7)))
+
+ sub_graph_nodes = sub_graph_between_nodes(graph, [1], [2])
+ self.assertIsNotNone(sub_graph_nodes)
+ self.assertListEqual(sorted(sub_graph_nodes), [1, 2, 5, 6])
+
+ def test_sub_graph_between_nodes_do_not_include_incoming_edges_for_input_nodes(self):
+ """
+ Check that the function doesn't add input nodes for the start nodes of the sub-graph. For example, we do not
+ need to add node 5 in the case below if we find match from node 1 till node 4.
+ 5->
+ \
+ 1 -> 2 -> 3 -> 4
+ """
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 6)))
+ graph.add_edges_from([(1, 2), (2, 3), (3, 4), (5, 2)])
+ sub_graph_nodes = sub_graph_between_nodes(graph, [2], [4])
+ self.assertIsNotNone(sub_graph_nodes)
+ self.assertListEqual(sorted(sub_graph_nodes), [2, 3, 4])
+
+ def test_sub_graph_between_nodes_placeholder_included(self):
+ """
+ Check that the function doesn't allow to add Placeholders to the sub-graph. 5 is the Placeholder op.
+ 5->
+ \
+ 1 -> 2 -> 3 -> 4
+ """
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 6)))
+ graph.node[5]['op'] = 'Placeholder'
+ graph.add_edges_from([(1, 2), (2, 3), (3, 4), (5, 2)])
+ self.assertRaises(Error, sub_graph_between_nodes, graph, [1], [4])
+
+ def test_sub_graph_between_nodes_placeholder_excluded(self):
+ """
+ Check that the function do not check that node is Placeholders for the nodes not included into the sub-graph.
+ For example, node 5 is Placeholder but it is not included into the sub-graph, so this attribute is ignored.
+ 5->
+ \
+ 1 -> 2 -> 3 -> 4
+ """
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 6)))
+ graph.node[5]['op'] = 'Placeholder'
+ graph.add_edges_from([(1, 2), (2, 3), (3, 4), (5, 2)])
+ sub_graph_nodes = sub_graph_between_nodes(graph, [2], [4])
+ self.assertIsNotNone(sub_graph_nodes)
+ self.assertListEqual(sorted(sub_graph_nodes), [2, 3, 4])
+
+ def test_sub_graph_between_nodes_multiple_inputs(self):
+ """
+ Check that the function works correctly when multiple inputs specified.
+ 5->
+ \
+ 1 -> 2 -> 3 -> 4
+ """
+ graph = nx.MultiDiGraph()
+ graph.add_nodes_from(list(range(1, 6)))
+ graph.add_edges_from([(1, 2), (2, 3), (3, 4), (5, 2)])
+ sub_graph_nodes = sub_graph_between_nodes(graph, [2, 5], [4])
+ self.assertIsNotNone(sub_graph_nodes)
+ self.assertListEqual(sorted(sub_graph_nodes), sorted([2, 3, 4, 5]))
+
+ def test_sub_graph_between_nodes_branches_included(self):
+ """
+ Check that the function works correctly for tree like structures.
+ 1 -> 2 -> 3 -> 4
+ \
+ 5 -> 6
+ / \
+ 9 -> -> 7 -> 8
+ """
+ graph = nx.MultiDiGraph()
+ node_names = list(range(1, 10))
+ graph.add_nodes_from(node_names)
+ graph.add_edges_from([(1, 2), (2, 3), (3, 4), (2, 5), (5, 6), (5, 7), (7, 8), (9, 5)])
+ self.assertListEqual(sorted(sub_graph_between_nodes(graph, [1], [4])), node_names)
+ self.assertListEqual(sorted(sub_graph_between_nodes(graph, [1], [6])), node_names)
+ self.assertListEqual(sorted(sub_graph_between_nodes(graph, [1], [8])), node_names)
+ # all nodes except 4 because it is a child of end node
+ self.assertListEqual(sorted(sub_graph_between_nodes(graph, [1], [3])), [n for n in node_names if n != 4])
+ # all nodes except 1 because it is a parent node child of start node. The nodes 3 and 4 must be added because
+ # after merging node 2 into sub-graph the node 2 will be removed and it is not known how to calculate the tensor
+ # between node 2 and 3.
+ self.assertListEqual(sorted(sub_graph_between_nodes(graph, [2], [8])), [n for n in node_names if n != 1])
diff --git a/model-optimizer/mo/utils/pipeline_config_test.py b/model-optimizer/mo/utils/pipeline_config_test.py
new file mode 100644
index 000000000..596a714d4
--- /dev/null
+++ b/model-optimizer/mo/utils/pipeline_config_test.py
@@ -0,0 +1,150 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import os
+import tempfile
+import unittest
+
+from mo.utils.error import Error
+from mo.utils.pipeline_config import PipelineConfig
+
+file_content = """model {
+ faster_rcnn {
+ num_classes: 90
+ image_resizer {
+ keep_aspect_ratio_resizer {
+ min_dimension: 600
+ max_dimension: 1024
+ }
+ }
+ feature_extractor {
+ type: "faster_rcnn_inception_v2"
+ first_stage_features_stride: 16
+ }
+ first_stage_anchor_generator {
+ grid_anchor_generator {
+ height_stride: 16
+ width_stride: 16
+ scales: 0.25
+ scales: 0.5
+ scales: 1.0
+ scales: 2.0
+ aspect_ratios: 0.5
+ aspect_ratios: 1.0
+ aspect_ratios: 2.0
+ }
+ }
+ first_stage_box_predictor_conv_hyperparams {
+ op: CONV
+ regularizer {
+ l2_regularizer {
+ weight: 0.0
+ }
+ }
+ initializer {
+ truncated_normal_initializer {
+ stddev: 0.00999999977648
+ }
+ }
+ }
+ first_stage_nms_score_threshold: 0.0
+ first_stage_nms_iou_threshold: 0.699999988079
+ first_stage_max_proposals: 100
+ first_stage_localization_loss_weight: 2.0
+ first_stage_objectness_loss_weight: 1.0
+ initial_crop_size: 14
+ maxpool_kernel_size: 2
+ maxpool_stride: 2
+ second_stage_box_predictor {
+ mask_rcnn_box_predictor {
+ fc_hyperparams {
+ op: FC
+ regularizer {
+ l2_regularizer {
+ weight: 0.0
+ }
+ }
+ initializer {
+ variance_scaling_initializer {
+ factor: 1.0
+ uniform: true
+ mode: FAN_AVG
+ }
+ }
+ }
+ }
+ }
+ use_dropout: false
+ dropout_keep_probability: 1.0
+ }
+ }
+ second_stage_post_processing {
+ batch_non_max_suppression {
+ score_threshold: 0.300000011921
+ iou_threshold: 0.600000023842
+ max_detections_per_class: 100
+ max_total_detections: 100
+ }
+ score_converter: SOFTMAX
+ }
+ second_stage_localization_loss_weight: 2.0
+ second_stage_classification_loss_weight: 1.0
+ }
+}
+"""
+
+
+class TestingSimpleProtoParser(unittest.TestCase):
+ def test_pipeline_config_not_existing_file(self):
+ self.assertRaises(Error, PipelineConfig, "/abc/def")
+
+ def test_pipeline_config_non_model_file(self):
+ file = tempfile.NamedTemporaryFile('wt', delete=False)
+ file.write("non_model {}")
+ file_name = file.name
+ file.close()
+
+ self.assertRaises(Error, PipelineConfig, file_name)
+
+ def test_pipeline_config_existing_file(self):
+ file = tempfile.NamedTemporaryFile('wt', delete=False)
+ file.write(file_content)
+ file_name = file.name
+ file.close()
+
+ pipeline_config = PipelineConfig(file_name)
+ expected_result = {'resizer_min_dimension': 600,
+ 'first_stage_nms_score_threshold': 0.0,
+ 'anchor_generator_aspect_ratios': [0.5, 1.0, 2.0],
+ 'num_classes': 90,
+ 'anchor_generator_scales': [0.25, 0.5, 1.0, 2.0],
+ 'first_stage_max_proposals': 100,
+ 'first_stage_nms_iou_threshold': 0.699999988079,
+ 'resizer_max_dimension': 1024,
+ 'initial_crop_size': 14,
+ 'frcnn_variance_height': 5.0,
+ 'frcnn_variance_width': 5.0,
+ 'frcnn_variance_x': 10.0,
+ 'frcnn_variance_y': 10.0,
+ 'ssd_anchor_generator_base_anchor_width': 1.0,
+ 'ssd_anchor_generator_base_anchor_height': 1.0,
+ 'anchor_generator_height': 256,
+ 'anchor_generator_width': 256,
+ 'anchor_generator_height_stride': 16,
+ 'anchor_generator_width_stride': 16,
+ }
+ os.unlink(file_name)
+ self.assertDictEqual(pipeline_config._model_params, expected_result)
diff --git a/model-optimizer/mo/utils/simple_proto_parser_test.py b/model-optimizer/mo/utils/simple_proto_parser_test.py
new file mode 100644
index 000000000..2f601ce5c
--- /dev/null
+++ b/model-optimizer/mo/utils/simple_proto_parser_test.py
@@ -0,0 +1,200 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import os
+import tempfile
+import unittest
+
+from mo.utils.simple_proto_parser import SimpleProtoParser
+
+correct_proto_message_1 = 'model { faster_rcnn { num_classes: 90 image_resizer { keep_aspect_ratio_resizer {' \
+ ' min_dimension: 600 max_dimension: 1024 }}}}'
+
+correct_proto_message_2 = ' first_stage_anchor_generator {grid_anchor_generator {height_stride: 16 width_stride:' \
+ ' 16 scales: 0.25 scales: 0.5 scales: 1.0 scales: 2.0 aspect_ratios: 0.5 aspect_ratios:' \
+ ' 1.0 aspect_ratios: 2.0}}'
+
+correct_proto_message_3 = ' initializer \n{variance_scaling_initializer \n{\nfactor: 1.0 uniform: true bla: false ' \
+ 'mode: FAN_AVG}}'
+
+correct_proto_message_4 = 'train_input_reader {label_map_path: "PATH_TO_BE_CONFIGURED/mscoco_label_map.pbtxt"' \
+ ' tf_record_input_reader { input_path: "PATH_TO_BE_CONFIGURED/ mscoco_train.record" }}'
+
+correct_proto_message_5 = ' initializer \n # abc \n{variance_scaling_initializer \n{\nfactor: 1.0 \n # sd ' \
+ '\nuniform: true bla: false mode: FAN_AVG}}'
+
+correct_proto_message_6 = ' first_stage_anchor_generator {grid_anchor_generator {height_stride: 16 width_stride:' \
+ ' 16 scales: [ 0.25, 0.5, 1.0, 2.0] aspect_ratios: 0.5 aspect_ratios:' \
+ ' 1.0 aspect_ratios: 2.0}}'
+
+correct_proto_message_7 = ' first_stage_anchor_generator {grid_anchor_generator {height_stride: 16 width_stride:' \
+ ' 16 scales: [ 0.25, 0.5, 1.0, 2.0] aspect_ratios: [] }}'
+
+correct_proto_message_8 = 'model {good_list: [3.0, 5.0, ]}'
+
+correct_proto_message_9 = ' first_stage_anchor_generator {grid_anchor_generator {height_stride: 16, width_stride:' \
+ ' 16 scales: [ 0.25, 0.5, 1.0, 2.0], aspect_ratios: [] }}'
+
+correct_proto_message_10 = 'train_input_reader {label_map_path: "C:\mscoco_label_map.pbtxt"' \
+ ' tf_record_input_reader { input_path: "PATH_TO_BE_CONFIGURED/ mscoco_train.record" }}'
+
+correct_proto_message_11 = 'model {path: "C:\[{],}" other_value: [1, 2, 3, 4]}'
+
+incorrect_proto_message_1 = 'model { bad_no_value }'
+
+incorrect_proto_message_2 = 'model { abc: 3 { }'
+
+incorrect_proto_message_3 = 'model { too_many_values: 3 4 }'
+
+incorrect_proto_message_4 = 'model { missing_values: '
+
+incorrect_proto_message_5 = 'model { missing_values: aa bb : }'
+
+incorrect_proto_message_6 = 'model : '
+
+incorrect_proto_message_7 = 'model : {bad_list: [3.0, 4, , 4.0]}'
+
+
+class TestingSimpleProtoParser(unittest.TestCase):
+ def test_correct_proto_reader_from_string_1(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_1)
+ expected_result = {'model': {'faster_rcnn': {'num_classes': 90, 'image_resizer': {
+ 'keep_aspect_ratio_resizer': {'min_dimension': 600, 'max_dimension': 1024}}}}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_2(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_2)
+ expected_result = {'first_stage_anchor_generator': {
+ 'grid_anchor_generator': {'height_stride': 16, 'width_stride': 16, 'scales': [0.25, 0.5, 1.0, 2.0],
+ 'aspect_ratios': [0.5, 1.0, 2.0]}}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_3(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_3)
+ expected_result = {
+ 'initializer': {
+ 'variance_scaling_initializer': {'factor': 1.0, 'uniform': True, 'bla': False, 'mode': 'FAN_AVG'}}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_4(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_4)
+ expected_result = {
+ 'train_input_reader': {'label_map_path': "PATH_TO_BE_CONFIGURED/mscoco_label_map.pbtxt",
+ 'tf_record_input_reader': {
+ 'input_path': "PATH_TO_BE_CONFIGURED/ mscoco_train.record"}}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_with_comments(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_5)
+ expected_result = {
+ 'initializer': {
+ 'variance_scaling_initializer': {'factor': 1.0, 'uniform': True, 'bla': False, 'mode': 'FAN_AVG'}}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_with_lists(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_6)
+ expected_result = {'first_stage_anchor_generator': {
+ 'grid_anchor_generator': {'height_stride': 16, 'width_stride': 16, 'scales': [0.25, 0.5, 1.0, 2.0],
+ 'aspect_ratios': [0.5, 1.0, 2.0]}}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_with_empty_list(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_7)
+ expected_result = {'first_stage_anchor_generator': {
+ 'grid_anchor_generator': {'height_stride': 16, 'width_stride': 16, 'scales': [0.25, 0.5, 1.0, 2.0],
+ 'aspect_ratios': []}}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_with_comma_trailing_list(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_8)
+ expected_result = {'model': {'good_list': [3.0, 5.0]}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_with_redundant_commas(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_9)
+ expected_result = {'first_stage_anchor_generator': {
+ 'grid_anchor_generator': {'height_stride': 16, 'width_stride': 16, 'scales': [0.25, 0.5, 1.0, 2.0],
+ 'aspect_ratios': []}}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_with_windows_path(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_10)
+ expected_result = {
+ 'train_input_reader': {'label_map_path': "C:\mscoco_label_map.pbtxt",
+ 'tf_record_input_reader': {
+ 'input_path': "PATH_TO_BE_CONFIGURED/ mscoco_train.record"}}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_correct_proto_reader_from_string_with_special_characters_in_string(self):
+ result = SimpleProtoParser().parse_from_string(correct_proto_message_11)
+ expected_result = {'model': {'path': "C:\[{],}",
+ 'other_value': [1, 2, 3, 4]}}
+ self.assertDictEqual(result, expected_result)
+
+ def test_incorrect_proto_reader_from_string_1(self):
+ result = SimpleProtoParser().parse_from_string(incorrect_proto_message_1)
+ self.assertIsNone(result)
+
+ def test_incorrect_proto_reader_from_string_2(self):
+ result = SimpleProtoParser().parse_from_string(incorrect_proto_message_2)
+ self.assertIsNone(result)
+
+ def test_incorrect_proto_reader_from_string_3(self):
+ result = SimpleProtoParser().parse_from_string(incorrect_proto_message_3)
+ self.assertIsNone(result)
+
+ def test_incorrect_proto_reader_from_string_4(self):
+ result = SimpleProtoParser().parse_from_string(incorrect_proto_message_4)
+ self.assertIsNone(result)
+
+ def test_incorrect_proto_reader_from_string_5(self):
+ result = SimpleProtoParser().parse_from_string(incorrect_proto_message_5)
+ self.assertIsNone(result)
+
+ def test_incorrect_proto_reader_from_string_6(self):
+ result = SimpleProtoParser().parse_from_string(incorrect_proto_message_6)
+ self.assertIsNone(result)
+
+ def test_incorrect_proto_reader_from_string_7(self):
+ result = SimpleProtoParser().parse_from_string(incorrect_proto_message_7)
+ self.assertIsNone(result)
+
+ def test_correct_proto_reader_from_file(self):
+ file = tempfile.NamedTemporaryFile('wt', delete=False)
+ file.write(correct_proto_message_1)
+ file_name = file.name
+ file.close()
+
+ result = SimpleProtoParser().parse_file(file_name)
+ expected_result = {'model': {'faster_rcnn': {'num_classes': 90, 'image_resizer': {
+ 'keep_aspect_ratio_resizer': {'min_dimension': 600, 'max_dimension': 1024}}}}}
+ self.assertDictEqual(result, expected_result)
+ os.unlink(file_name)
+
+ def test_proto_reader_from_non_readable_file(self):
+ file = tempfile.NamedTemporaryFile('wt', delete=False)
+ file.write(correct_proto_message_1)
+ file_name = file.name
+ file.close()
+ os.chmod(file_name, 0000)
+
+ result = SimpleProtoParser().parse_file(file_name)
+ self.assertIsNone(result)
+ os.unlink(file_name)
+
+ def test_proto_reader_from_non_existing_file(self):
+ result = SimpleProtoParser().parse_file('/non/existing/file')
+ self.assertIsNone(result)
diff --git a/model-optimizer/mo/utils/summarize_graph_test.py b/model-optimizer/mo/utils/summarize_graph_test.py
new file mode 100644
index 000000000..fbed0ebae
--- /dev/null
+++ b/model-optimizer/mo/utils/summarize_graph_test.py
@@ -0,0 +1,38 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch, mock_open
+
+from mo.front.tf.loader import load_tf_graph_def
+from mo.utils.summarize_graph import summarize_graph
+
+pbtxt = 'node{name:"Placeholder"op:"Placeholder"attr{key:"dtype"value{type:DT_FLOAT}}attr{key:"shape"value{shape{dim' + \
+ '{size:1}dim{size:227}dim{size:227}dim{size:3}}}}}node{name:"Output/Identity"op:"Identity"input:"Placeholder' + \
+ '"attr{key:"T"value{type:DT_FLOAT}}}'
+
+
+class TestingSummarizeGraph(unittest.TestCase):
+ def test_summarize_graph(self):
+ with patch('mo.front.tf.loader.open', mock_open(read_data=pbtxt)) as m:
+ graph_def, _ = load_tf_graph_def('path', False)
+ summary = summarize_graph(graph_def)
+ self.assertEqual(len(summary['outputs']), 1)
+ self.assertEqual(summary['outputs'][0], 'Output/Identity')
+ self.assertEqual(len(summary['inputs']), 1)
+ self.assertEqual('Placeholder' in summary['inputs'], True)
+ self.assertEqual(str(summary['inputs']['Placeholder']['shape']), '(1,227,227,3)')
+ self.assertEqual(str(summary['inputs']['Placeholder']['type']), 'float32')
diff --git a/model-optimizer/mo/utils/unittest/extractors.py b/model-optimizer/mo/utils/unittest/extractors.py
new file mode 100644
index 000000000..e58534c02
--- /dev/null
+++ b/model-optimizer/mo/utils/unittest/extractors.py
@@ -0,0 +1,82 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+from unittest.mock import patch
+
+import numpy as np
+
+
+class PB(dict):
+ __getattr__ = dict.get
+
+
+class BaseExtractorsTestingClass(unittest.TestCase):
+ expected = None
+ res = None
+ call_args = None
+ expected_call_args = None
+
+ def setUp(self):
+ if hasattr(self, 'patcher') and self.patcher: # pylint: disable=no-member
+ patcher = patch(self.patcher) # pylint: disable=no-member
+ self.addCleanup(patcher.stop)
+ self.infer_mock = patcher.start()
+
+ def compare(self):
+ if hasattr(self, 'infer_mock'):
+ self.assertTrue(self.infer_mock.called)
+ for key, val in self.expected.items():
+ if key == "infer":
+ self.assertEqual(self.call_args, self.expected_call_args)
+ if type(val) is np.ndarray:
+ np.testing.assert_equal(val, self.res[key])
+ elif type(val) is list:
+ self.assertTrue(np.all([val == self.res[key]]))
+ else:
+ self.assertEqual(val, self.res[key],
+ "{} attribute comparison failed! Expected {} but {} given.".format(key, val,
+ self.res[key]))
+
+
+class FakeParam:
+ def __init__(self, param_key, param_val):
+ setattr(self, param_key, param_val)
+
+
+class FakeMultiParam:
+ def __init__(self, dict_values):
+ self.dict_values = dict_values
+ for (key, value) in dict_values.items():
+ # if type(value) != dict:
+ setattr(self, key, value)
+ # else:
+ # setattr(self, key, FakeMultiParam(value))
+
+
+class FakeBlob:
+ def __init__(self, param_key, param_val):
+ setattr(self, param_key, param_val)
+
+
+class FakeModelLayer:
+ def __init__(self, blobs_val):
+ self.blobs = [FakeBlob('data', val) for val in blobs_val]
+
+
+class FakeValue:
+ def __init__(self, val):
+ self.shape = val \ No newline at end of file
diff --git a/model-optimizer/mo/utils/unittest/graph.py b/model-optimizer/mo/utils/unittest/graph.py
new file mode 100644
index 000000000..64a0f3027
--- /dev/null
+++ b/model-optimizer/mo/utils/unittest/graph.py
@@ -0,0 +1,321 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+from collections import deque
+from copy import deepcopy
+
+import networkx as nx
+import numpy as np
+
+from mo.graph.graph import Node
+from mo.middle.pattern_match import all_edges_in_nodes
+from mo.utils.error import Error
+
+
+def not_all_new(old_elements: list, new_elements: list):
+ """
+ This function check whether at least one element from new_elements are in old_elements.
+ """
+ return any([element in old_elements for element in new_elements])
+
+
+def check_and_update_ports(edges_data: list, in_port: bool = True):
+ key = 'in' if in_port else 'out'
+ key_in_edges = [key in edge_data for edge_data in edges_data]
+ if all(key_in_edges):
+ ports = [edge_data[key] for edge_data in edges_data]
+ if len(ports) != len(set(ports)):
+ raise Error("Please, provide unique {} ports for nodes".format(key))
+ elif not any(key_in_edges):
+ for i, edge_data in enumerate(edges_data):
+ edge_data[key] = i
+ else:
+ raise Error("Please, provide all {} ports for nodes".format(key))
+
+
+def build_graph_with_attrs(nodes_with_attrs: list, edges_with_attrs: list, new_nodes_with_attrs: list = [],
+ new_edges_with_attrs: list = [], update_edge_attrs: dict = None,
+ update_nodes_attributes: dict = None, nodes_with_edges_only: bool = False,
+ add_nodes_from_edges: bool = False):
+ """
+ Build the nx.MultiDiGraph with specific nodes and edges. Also update of edge and node parameters is supported.
+ :param nodes_with_attrs: list of tuples ('node_name', {node_attrs})
+ :param edges_with_attrs: list of tuples like (start node, end node, (optional) {attrs of the edge}).
+ :param new_nodes_with_attrs: analogically nodes_with_attrs
+ :param new_edges_with_attrs: analogically new_edges
+ :param update_edge_attrs: optional dictionary like {('from_node', 'to_node', key): {edge_attrs}}.
+ :param update_nodes_attributes: optional dictionary which specifies nodes names and their attributes to be updated. The
+ key is a node name to update attribute and the value is a dictionary with attribute name and its value.
+ :param nodes_with_edges_only: add nodes which has at least one incoming or outcoming edge.
+ :param add_nodes_from_edges: whether nodes that is not listed in all_nodes but are in all_edges is allowed.
+ :return: generated graph.
+ """
+ if not_all_new([node[0] for node in nodes_with_attrs], [node[0] for node in new_nodes_with_attrs]):
+ raise Error('Some nodes from new_nodes_with_attrs are already in nodes.'
+ ' Please, add to new_nodes_with_attrs only NEW nodes.')
+
+ if not_all_new([(edge[0], edge[1]) for edge in edges_with_attrs], [(edge[0], edge[1]) for edge in new_edges_with_attrs]):
+ raise Error('Some edges from new_edges_with_attrs are already in edges.'
+ ' Please, add to new_edges_with_attrs only NEW edges.')
+
+ # Check that all nodes from list of edges are in nodes
+ all_nodes = nodes_with_attrs + new_nodes_with_attrs
+ all_edges = edges_with_attrs + new_edges_with_attrs
+ all_nodes_names = [node[0] for node in all_nodes]
+ if not add_nodes_from_edges and not all_edges_in_nodes(nodes=all_nodes_names, edges=all_edges):
+ raise Error("Some nodes from list of edges is not in nodes. Please, add all necessary nodes.")
+
+ graph = nx.MultiDiGraph()
+
+ # Create dict for nodes with attrs
+ nodes_attrs = {}
+ for node_name, attrs in all_nodes:
+ nodes_attrs[node_name] = attrs
+ if 'name' not in attrs:
+ attrs['name'] = node_name
+
+ if nodes_with_edges_only:
+ # filter nodes to keep only ones with edges connected
+ filtered_nodes = {}
+ for edge in all_edges:
+ node_1, node_2 = edge[0], edge[1]
+ filtered_nodes[node_1] = nodes_attrs[node_1]
+ filtered_nodes[node_2] = nodes_attrs[node_2]
+ nodes_attrs = filtered_nodes
+
+ # Create all nodes
+ for node, attrs in nodes_attrs.items():
+ graph.add_node(node, **deepcopy(attrs))
+
+ # Connect nodes with edges (also unpack edge params)
+ for edge in all_edges:
+ node_1, node_2 = edge[0], edge[1]
+ edge_attrs = edge[2] if len(edge) == 3 else {}
+ graph.add_edge(node_1, node_2, **edge_attrs)
+
+ # Update attributes of edges
+ if update_edge_attrs:
+ # it will work in 2.x networkx only
+ for edge, attr in update_edge_attrs.items():
+ for k, v in attr.items():
+ nx.set_edge_attributes(G=graph, name=k, values={edge: v})
+
+ # Update attributes of nodes
+ if update_nodes_attributes is not None:
+ for node_name, new_attrs in update_nodes_attributes:
+ assert (node_name in graph.nodes())
+ for attr, value in new_attrs.items():
+ graph.node[node_name][attr] = value
+
+ for node in graph.nodes():
+ check_and_update_ports([graph.get_edge_data(edge[0], node)[0] for edge in graph.in_edges(node)], True)
+ check_and_update_ports([graph.get_edge_data(node, edge[1])[0] for edge in graph.out_edges(node)], False)
+
+ return graph
+
+
+def build_graph(nodes_attrs: dict, edges: list, update_attributes: dict = None, nodes_with_edges_only: bool = False):
+ """
+ Build the nx.MultiDiGraph with specific nodes and edges.
+ :param nodes_attrs: dictionary where key is the node name and the value is the dictionary with node attributes.
+ :param edges: list of pairs with start and end node names of the edge.
+ :param update_attributes: optional dictionary which specifies nodes names and their attributes to be updated. The
+ key is a node name to update attribute and the value is a dictionary with attribute name and its value.
+ :param nodes_with_edges_only: add nodes which has at least one incoming or outcoming edge.
+ :return: generated graph.
+ """
+ graph = nx.MultiDiGraph()
+
+ for node_name, attrs in nodes_attrs.items():
+ if 'name' not in attrs:
+ attrs['name'] = node_name
+
+ if nodes_with_edges_only:
+ # filter nodes to keep only ones with edges connected
+ filtered_nodes = {}
+ for item in edges:
+ if len(item) == 2: # TODO: is there any better way in python to do that?
+ node1, node2 = item
+ else:
+ node1, node2, _ = item
+ filtered_nodes[node1] = nodes_attrs[node1]
+ filtered_nodes[node2] = nodes_attrs[node2]
+ nodes_attrs = filtered_nodes
+
+ # create all nodes first
+ for node, attrs in nodes_attrs.items():
+ assert node not in graph.nodes()
+ graph.add_node(node, **deepcopy(attrs))
+
+ # connect nodes with edges
+ for item in edges:
+ if len(item) == 2: # TODO: is there any better way in python to do that?
+ node_1, node_2 = item
+ edge_attrs = {}
+ else:
+ node_1, node_2, edge_attrs = item
+
+ common_attrs = {'in': len(graph.in_edges(node_2)),
+ 'out': len(graph.out_edges(node_1)),
+ 'name': nodes_attrs[node_1]['name']}
+ common_attrs.update(edge_attrs)
+ graph.add_edge(node_1, node_2, **common_attrs)
+
+ if update_attributes is not None:
+ for node_name, new_attrs in update_attributes.items():
+ assert (node_name in graph.nodes())
+ for attr, value in new_attrs.items():
+ graph.node[node_name][attr] = value
+
+ return graph
+
+
+def build_graph_with_edge_attrs(nodes_attrs: dict, edges: list, update_attributes: dict = None):
+ """
+ Build the nx.MultiDiGraph with specific nodes and edges.
+ :param nodes_attrs: dictionary where key is the node name and the value is the dictionary with node attributes.
+ :param edges: list of pairs with start and end node names of the edge.
+ :param update_attributes: optional dictionary which specifies nodes names and their attributes to be updated. The
+ key is a node name to update attribute and the value is a dictionary with attribute name and its value.
+ :return: generated graph.
+ """
+ graph = nx.MultiDiGraph()
+ for node_1, node_2, attr in edges:
+ if node_1 not in graph.nodes():
+ graph.add_node(node_1, **deepcopy(nodes_attrs[node_1]))
+ if node_2 not in graph.nodes():
+ graph.add_node(node_2, **deepcopy(nodes_attrs[node_2]))
+ graph.add_edge(node_1, node_2, **attr)
+ if update_attributes is not None:
+ for node_name, new_attrs in update_attributes.items():
+ assert (node_name in graph.nodes())
+ for attr, value in new_attrs.items():
+ graph.node[node_name][attr] = value
+ return graph
+
+
+def compare_graphs(graph: nx.MultiDiGraph, graph_ref: nx.MultiDiGraph, last_node: str, last_node_ref=None,
+ check_op_attrs=False):
+ if last_node_ref is None:
+ last_node_ref = last_node
+
+ q = deque([last_node])
+ q_ref = deque([last_node_ref])
+
+ checked_nodes = []
+ checked_nodes_ref = []
+
+ while len(q_ref) != 0:
+ if len(q) == 0:
+ return False, 'Graphs have different number of nodes'
+ node = Node(graph, q.popleft())
+ node_ref = Node(graph_ref, q_ref.popleft())
+
+ checked_nodes.append(node.id)
+ checked_nodes_ref.append(node_ref.id)
+
+ # Check that nodes has same amount of output nodes
+ if len(node_ref.out_nodes()) != len(node.out_nodes()):
+ return False, '{} and {} has different amount of output nodes'.format(node.id, node_ref.id)
+
+ # Check that nodes has same amount of input nodes
+ if len(node_ref.in_nodes()) != len(node.in_nodes()):
+ return False, '{} and {} has different amount of input nodes'.format(node.id, node_ref.id)
+
+ # Check that nodes has same 'kind'
+ if node_ref.kind != node.kind:
+ return False, '{} and {} has different kind parameter'.format(node.id, node_ref.id)
+
+ # Check can_be_fused attr
+ if node_ref.has_valid('can_be_fused'):
+ if node_ref.soft_get('can_be_fused') != node.soft_get('can_be_fused'):
+ return False, '{} and {} has different can_be_fused parameter {} and {}'.format(
+ node.id, node_ref.id, node.soft_get('can_be_fused'), node_ref.soft_get('can_be_fused'))
+
+ if node_ref.kind == 'op':
+ # Check that nodes has same operation
+ if check_op_attrs:
+ for attr in graph_ref.node[node_ref.id]:
+ if graph_ref.node[node_ref.id][attr] is None or attr in ['name', 'id']:
+ continue
+ if attr not in graph.node[node.id]:
+ return False, 'Node {} has missing attribute {}'.format(node.id, attr)
+
+ if type(graph_ref.node[node_ref.id][attr]) in [np.ndarray, list]:
+ if not np.array_equal(graph.node[node.id][attr], graph_ref.node[node_ref.id][attr]):
+ return False, '{} and {} has different attr {} : {} and {}'.format(
+ node.id, node_ref.id, attr, graph.node[node.id][attr],
+ graph_ref.node[node_ref.id][attr])
+ else:
+ if graph.node[node.id][attr] != graph_ref.node[node_ref.id][attr]:
+ return False, '{} and {} has different attr {} : {} and {}'.format(
+ node.id, node_ref.id, attr, graph.node[node.id][attr],
+ graph_ref.node[node_ref.id][attr])
+ else:
+ if node_ref.has_valid('shape') and not node.has_valid('shape'):
+ return False, '{} has None shape'.format(node.id)
+ if node_ref.has_valid('value') and not node.has_valid('value'):
+ return False, '{} has None value'.format(node.id)
+
+ # Check that nodes has same shape and value
+ if node_ref.has_valid('shape') and node_ref.shape is not None and not np.array_equal(node_ref.shape,
+ node.shape):
+ return False, '{} and {} has different shapes {} and {}'.format(node.id, node_ref.id, node.shape,
+ node_ref.shape)
+ if node_ref.has_valid('value') and node_ref.value is not None and not np.allclose(node_ref.value,
+ node.value, rtol=1e-05,
+ atol=1e-08):
+ return False, '{} and {} has different values \n{} \nand \n{}'.format(node.id, node_ref.id, node.value,
+ node_ref.value)
+ ports = sorted(node.in_nodes().keys()) if node.kind == 'op' else None
+ in_nodes = [node.in_node(k) for k in ports] if node.kind == 'op' else node.in_nodes()
+ for in_node in in_nodes:
+ if in_node.id not in checked_nodes and in_node.id not in q:
+ q.append(in_node.id)
+
+ ports_ref = sorted(node_ref.in_nodes().keys()) if node_ref.kind == 'op' else None
+ if ports != ports_ref:
+ return False, '{} and {} has different ports'.format(node.id, node_ref.id)
+
+ in_nodes = [node_ref.in_node(k) for k in ports] if node_ref.kind == 'op' else node_ref.in_nodes()
+ for in_node in in_nodes:
+ 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 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 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)
+
+ return True, ''
+
+
+class FakeNode:
+ def __init__(self, pl, ml):
+ self.pb = pl
+ self.model_pb = ml
+ self.graph = None
+
+ def __setitem__(self, key, value):
+ setattr(self, key, value)
+
+ def __getitem__(self, item):
+ return getattr(self, item)
diff --git a/model-optimizer/mo/utils/utils_test.py b/model-optimizer/mo/utils/utils_test.py
new file mode 100644
index 000000000..7ebae7f01
--- /dev/null
+++ b/model-optimizer/mo/utils/utils_test.py
@@ -0,0 +1,41 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+import networkx as nx
+import numpy as np
+from mo.utils.utils import match_shapes
+
+
+class TestMatchShapes(unittest.TestCase):
+
+ def run_match_shapes(self, pattern: list, shape: list):
+ return match_shapes(np.array(pattern, dtype=np.int64), np.array(shape, dtype=np.int64))
+
+ def test_positive(self):
+ self.assertTrue(self.run_match_shapes([], []))
+ self.assertTrue(self.run_match_shapes([1,2,3], [1,2,3]))
+ self.assertTrue(self.run_match_shapes([-1,2,3], [1,2,3]))
+ self.assertTrue(self.run_match_shapes([1,-1,3], [1,2,3]))
+ self.assertTrue(self.run_match_shapes([-1,-1,-1], [1,2,3]))
+ self.assertTrue(self.run_match_shapes([-1], [2]))
+
+ def test_negative(self):
+ self.assertFalse(self.run_match_shapes([-1], []))
+ self.assertFalse(self.run_match_shapes([-1], [1,2,3]))
+ self.assertFalse(self.run_match_shapes([-1,2,3], [1,3,3]))
+ self.assertFalse(self.run_match_shapes([1,-1,3], [2,2]))
+ self.assertFalse(self.run_match_shapes([-1, -1, -1], [2, 3, 4, 5]))
diff --git a/model-optimizer/mo/utils/version_test.py b/model-optimizer/mo/utils/version_test.py
new file mode 100644
index 000000000..909e7425e
--- /dev/null
+++ b/model-optimizer/mo/utils/version_test.py
@@ -0,0 +1,24 @@
+"""
+ Copyright (c) 2018 Intel Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+"""
+
+import unittest
+
+from mo.utils.version import get_version
+
+
+class TestingVersion(unittest.TestCase):
+ def test_unknown_version(self):
+ self.assertEqual(get_version(), "unknown version")