Add Model Optimizer --transform option (#5504)

* Execute MO stages inside subprocess to have single IE check

* Add --transform key

* Updated ofline transformations to execute user specified passes; updated logic to raise when --transform is used

* Parametrize LowLatency transformation with num_iterations key

* Fixed MO and IE versions comparision

* Use subprocess for offline transformations execution to catch errors

* remove ie_is_available from IR; fixed typo

* Fix for old IE versions

* Update parse_transform key unit tests

* Show available transformations

* Fixed typo

* Fix review comments

* Fix python2 compatibility

* Fixed review comments

* Fixed __main__ import
This commit is contained in:
Gleb Kazantaev
2021-05-12 18:42:56 +03:00
committed by GitHub
parent ed4d3fc4ed
commit b4565b7b4f
25 changed files with 385 additions and 130 deletions
@@ -6,6 +6,7 @@ from ..inference_engine.ie_api cimport IENetwork
from libcpp cimport bool
from libcpp.string cimport string
from libc.stdint cimport int64_t
def ApplyMOCTransformations(IENetwork network, bool cf):
@@ -16,8 +17,8 @@ def ApplyPOTTransformations(IENetwork network, string device):
C.ApplyPOTTransformations(network.impl, device)
def ApplyLowLatencyTransformation(IENetwork network):
C.ApplyLowLatencyTransformation(network.impl)
def ApplyLowLatencyTransformation(IENetwork network, int64_t num_iterations=1):
C.ApplyLowLatencyTransformation(network.impl, num_iterations)
def ApplyPruningTransformation(IENetwork network):
@@ -26,8 +26,9 @@ void InferenceEnginePython::ApplyPOTTransformations(InferenceEnginePython::IENet
manager.run_passes(network.actual->getFunction());
}
void InferenceEnginePython::ApplyLowLatencyTransformation(InferenceEnginePython::IENetwork network) {
void InferenceEnginePython::ApplyLowLatencyTransformation(InferenceEnginePython::IENetwork network, int64_t num_iterations) {
ngraph::pass::Manager manager;
// TODO: pass num_iterations to LowLatency
manager.register_pass<ngraph::pass::LowLatency>();
manager.register_pass<ngraph::pass::UnrollTensorIterator>();
@@ -15,7 +15,7 @@ void ApplyMOCTransformations(InferenceEnginePython::IENetwork network, bool cf);
void ApplyPOTTransformations(InferenceEnginePython::IENetwork network, std::string device);
void ApplyLowLatencyTransformation(InferenceEnginePython::IENetwork network);
void ApplyLowLatencyTransformation(InferenceEnginePython::IENetwork network, int64_t num_iterations);
void ApplyPruningTransformation(InferenceEnginePython::IENetwork network);
@@ -3,6 +3,7 @@
from libcpp cimport bool
from libcpp.string cimport string
from libc.stdint cimport int64_t
from ..inference_engine.ie_api_impl_defs cimport IENetwork
@@ -11,7 +12,7 @@ cdef extern from "offline_transformations_api_impl.hpp" namespace "InferenceEngi
cdef void ApplyPOTTransformations(IENetwork network, string device)
cdef void ApplyLowLatencyTransformation(IENetwork network)
cdef void ApplyLowLatencyTransformation(IENetwork network, int64_t num_iterations)
cdef void ApplyPruningTransformation(IENetwork network)
@@ -939,6 +939,11 @@ mo/graph/graph.py
mo/graph/perm_inputs.py
mo/graph/port.py
mo/main.py
mo/main_caffe.py
mo/main_kaldi.py
mo/main_mxnet.py
mo/main_onnx.py
mo/main_tf.py
mo/middle/__init__.py
mo/middle/passes/__init__.py
mo/middle/passes/conv.py
@@ -1004,6 +1009,7 @@ mo/ops/unsqueeze.py
mo/pipeline/__init__.py
mo/pipeline/common.py
mo/pipeline/unified.py
mo/subprocess_main.py
mo/utils/__init__.py
mo/utils/broadcasting.py
mo/utils/check_ie_bindings.py
+2 -11
View File
@@ -3,16 +3,7 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.versions_checker import check_python_version # pylint: disable=no-name-in-module
if __name__ == "__main__":
ret_code = check_python_version()
if ret_code:
sys.exit(ret_code)
from mo.main import main
from mo.utils.cli_parser import get_all_cli_parser # pylint: disable=no-name-in-module
sys.exit(main(get_all_cli_parser(), None))
from mo.subprocess_main import subprocess_main # pylint: disable=no-name-in-module
subprocess_main(framework=None)
+2 -13
View File
@@ -1,16 +1,5 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.versions_checker import check_python_version # pylint: disable=no-name-in-module
ret_code = check_python_version()
if ret_code:
sys.exit(ret_code)
from mo.main import main
from mo.utils.cli_parser import get_all_cli_parser # pylint: disable=no-name-in-module
sys.exit(main(get_all_cli_parser(), None))
from mo.subprocess_main import subprocess_main
subprocess_main(framework=None)
@@ -3,28 +3,48 @@
import argparse
from mo.utils.error import Error
from mo.utils.cli_parser import parse_transform
def get_available_transformations():
try:
from openvino.offline_transformations import ApplyLowLatencyTransformation # pylint: disable=import-error
return {
'LowLatency': ApplyLowLatencyTransformation,
}
except Exception as e:
return {}
def apply_offline_transformations(input_model: str, framework: str, transforms: list):
# This variable is only needed by GenerateMappingFile transformation
# to produce correct mapping
extract_names = framework in ['tf', 'mxnet', 'kaldi']
from openvino.inference_engine import read_network # pylint: disable=import-error
from openvino.offline_transformations import ApplyMOCTransformations, GenerateMappingFile # pylint: disable=import-error
net = read_network(input_model + "_tmp.xml", input_model + "_tmp.bin")
available_transformations = get_available_transformations()
for name, args in transforms:
if name not in available_transformations.keys():
raise Error("Transformation {} is not available.".format(name))
available_transformations[name](net, **args)
net.serialize(input_model + ".xml", input_model + ".bin")
path_to_mapping = input_model + ".mapping"
GenerateMappingFile(net, path_to_mapping.encode('utf-8'), extract_names)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--input_model")
parser.add_argument("--framework")
parser.add_argument("--transform")
args = parser.parse_args()
path_to_model = args.input_model
# This variable is only needed by GenerateMappingFile transformation
# to produce correct mapping
extract_names = True if args.framework in ['tf', 'mxnet', 'kaldi'] else False
try:
from openvino.inference_engine import IECore, read_network # pylint: disable=import-error
from openvino.offline_transformations import ApplyMOCTransformations, GenerateMappingFile, CheckAPI # pylint: disable=import-error
except Exception as e:
print("[ WARNING ] {}".format(e))
exit(1)
CheckAPI()
net = read_network(path_to_model + "_tmp.xml", path_to_model + "_tmp.bin")
net.serialize(path_to_model + ".xml", path_to_model + ".bin")
path_to_mapping = path_to_model + ".mapping"
GenerateMappingFile(net, path_to_mapping.encode('utf-8'), extract_names)
apply_offline_transformations(args.input_model, args.framework, parse_transform(args.transform))
+35 -9
View File
@@ -6,8 +6,8 @@ import datetime
import logging as log
import os
import platform
import subprocess
import sys
import subprocess
import traceback
from collections import OrderedDict
from copy import deepcopy
@@ -24,7 +24,8 @@ from mo.pipeline.unified import unified_pipeline
from mo.utils import import_extensions
from mo.utils.cli_parser import get_placeholder_shapes, get_tuple_values, get_model_name, \
get_common_cli_options, get_caffe_cli_options, get_tf_cli_options, get_mxnet_cli_options, get_kaldi_cli_options, \
get_onnx_cli_options, get_mean_scale_dictionary, parse_tuple_pairs, get_freeze_placeholder_values, get_meta_info
get_onnx_cli_options, get_mean_scale_dictionary, parse_tuple_pairs, get_freeze_placeholder_values, get_meta_info, \
parse_transform, check_available_transforms
from mo.utils.error import Error, FrameworkError
from mo.utils.find_ie_version import find_ie_version
from mo.utils.get_ov_update_message import get_ov_update_message
@@ -33,7 +34,7 @@ from mo.utils.logger import init_logger
from mo.utils.model_analysis import AnalysisResults
from mo.utils.utils import refer_to_faq_msg
from mo.utils.version import get_version, get_simplified_mo_version, get_simplified_ie_version
from mo.utils.versions_checker import check_requirements
from mo.utils.versions_checker import check_requirements # pylint: disable=no-name-in-module
def replace_ext(name: str, old: str, new: str):
@@ -141,14 +142,22 @@ def prepare_ir(argv: argparse.Namespace):
# This try-except is additional reinsurance that the IE
# dependency search does not break the MO pipeline
try:
if not find_ie_version(silent=argv.silent) and not argv.silent:
argv.ie_is_available = find_ie_version(silent=argv.silent)
if not argv.ie_is_available and not argv.silent:
print("[ WARNING ] Could not find the Inference Engine Python API. At this moment, the Inference Engine dependency is not required, but will be required in future releases.")
print("[ WARNING ] Consider building the Inference Engine Python API from sources or try to install OpenVINO (TM) Toolkit using \"install_prerequisites.{}\"".format(
"bat" if sys.platform == "windows" else "sh"))
# If the IE was not found, it will not print the MO version, so we have to print it manually
print("{}: \t{}".format("Model Optimizer version", get_version()))
except Exception as e:
pass
argv.ie_is_available = False
# This is just to check that transform key is valid and transformations are available
check_available_transforms(parse_transform(argv.transform), argv.ie_is_available)
if argv.legacy_ir_generation and len(argv.transform) != 0:
raise Error("--legacy_ir_generation and --transform keys can not be used at the same time.")
ret_code = check_requirements(framework=argv.framework)
if ret_code:
@@ -250,6 +259,10 @@ def emit_ir(graph: Graph, argv: argparse.Namespace):
mean_data = deepcopy(graph.graph['mf']) if 'mf' in graph.graph else None
input_names = deepcopy(graph.graph['input_names']) if 'input_names' in graph.graph else []
# Remove temporary ie_is_available key from argv no to have it in IR
ie_is_available = argv.ie_is_available
del argv.ie_is_available
prepare_emit_ir(graph=graph,
data_type=graph.graph['cmd_params'].data_type,
output_dir=argv.output_dir,
@@ -270,16 +283,16 @@ def emit_ir(graph: Graph, argv: argparse.Namespace):
# This try-except is additional reinsurance that the IE
# dependency search does not break the MO pipeline
try:
if not argv.legacy_ir_generation and find_ie_version(silent=True):
if not argv.legacy_ir_generation and ie_is_available:
path_to_offline_transformations = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'back',
'offline_transformations.py')
status = subprocess.run([sys.executable, path_to_offline_transformations,
"--input_model", orig_model_name,
"--framework", argv.framework], env=os.environ, timeout=10)
"--framework", argv.framework,
"--transform", argv.transform], env=os.environ)
return_code = status.returncode
if return_code != 0 and not argv.silent:
log.error("offline_transformations return code {}".format(return_code), extra={'is_warning': True})
except Exception as e:
return_code = "failed"
log.error(e, extra={'is_warning': True})
message = str(dict({
@@ -296,6 +309,14 @@ def emit_ir(graph: Graph, argv: argparse.Namespace):
# produced by prepare_ir. This IR needs to be renamed from XXX_tmp.xml to XXX.xml
suffixes = [".xml", ".bin", ".mapping"]
if return_code != 0:
if len(argv.transform) != 0:
# Remove temporary IR before throwing exception
for suf in suffixes:
path_to_file = orig_model_name + "_tmp" + suf
if os.path.exists(path_to_file):
os.remove(path_to_file)
raise Error("Failed to apply transformations: {}".format(argv.transform))
log.error("Using fallback to produce IR.", extra={'is_warning': True})
for suf in suffixes:
# remove existing files
@@ -400,3 +421,8 @@ def main(cli_parser: argparse.ArgumentParser, framework: str):
telemetry.end_session()
telemetry.force_shutdown(1.0)
return 1
if __name__ == "__main__":
from mo.utils.cli_parser import get_all_cli_parser
sys.exit(main(get_all_cli_parser(), None))
+10
View File
@@ -0,0 +1,10 @@
# Copyright (C) 2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.cli_parser import get_caffe_cli_parser
if __name__ == "__main__":
from mo.main import main
sys.exit(main(get_caffe_cli_parser(), 'caffe'))
+10
View File
@@ -0,0 +1,10 @@
# Copyright (C) 2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.cli_parser import get_kaldi_cli_parser
if __name__ == "__main__":
from mo.main import main
sys.exit(main(get_kaldi_cli_parser(), 'kaldi'))
+10
View File
@@ -0,0 +1,10 @@
# Copyright (C) 2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.cli_parser import get_mxnet_cli_parser
if __name__ == "__main__":
from mo.main import main
sys.exit(main(get_mxnet_cli_parser(), 'mxnet'))
+10
View File
@@ -0,0 +1,10 @@
# Copyright (C) 2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.cli_parser import get_onnx_cli_parser
if __name__ == "__main__":
from mo.main import main
sys.exit(main(get_onnx_cli_parser(), 'onnx'))
+10
View File
@@ -0,0 +1,10 @@
# Copyright (C) 2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.cli_parser import get_tf_cli_parser
if __name__ == "__main__":
from mo.main import main
sys.exit(main(get_tf_cli_parser(), 'tf'))
+42
View File
@@ -0,0 +1,42 @@
# Copyright (C) 2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import os
import sys
import subprocess
from mo.utils.versions_checker import check_python_version # pylint: disable=no-name-in-module
def subprocess_main(framework=None):
"""
Please keep this file compatible with python2 in order to check user python version.
This function checks that Inference Engine Python API available and working as expected
and then in sub-process it executes main_<fw>.py files. Due to some OSs specifics we can't
just add paths to Python modules and libraries into current env. So to make Inference Engine
Python API to be available inside MO we need to use subprocess with new env.
"""
ret_code = check_python_version()
if ret_code:
sys.exit(ret_code)
from mo.utils.find_ie_version import find_ie_version
find_ie_version(silent=True)
mo_root_path = os.path.join(os.path.dirname(__file__), os.pardir)
python_path_key = 'PYTHONPATH'
if python_path_key not in os.environ:
os.environ[python_path_key] = mo_root_path
else:
os.environ[python_path_key] = os.pathsep.join([os.environ[python_path_key], mo_root_path])
path_to_main = os.path.join(os.path.realpath(os.path.dirname(__file__)),
'main_{}.py'.format(framework) if framework else 'main.py')
# python2 compatible code. Do not remove.
args = [sys.executable, path_to_main]
for arg in sys.argv[1:]:
args.append(arg)
status = subprocess.run(args, env=os.environ)
sys.exit(status.returncode)
+15 -11
View File
@@ -32,11 +32,20 @@ def send_telemetry(mo_version: str, message: str, event_type: str):
def import_core_modules(silent: bool, path_to_module: str):
try:
from openvino.inference_engine import IECore, get_version # pylint: disable=import-error
from openvino.offline_transformations import ApplyMOCTransformations, CheckAPI # pylint: disable=import-error
"""
This function checks that InferenceEngine Python API is available
and necessary python modules exists. So the next list of imports
must contain all IE/NG Python API imports that are used inside MO.
import openvino # pylint: disable=import-error
:param silent: enables or disables logs printing to stdout
:param path_to_module: path where python API modules were found
:return: True if all imports were successful and False otherwise
"""
try:
from openvino.inference_engine import get_version, read_network # pylint: disable=import-error
from openvino.offline_transformations import ApplyMOCTransformations, ApplyLowLatencyTransformation, GenerateMappingFile # pylint: disable=import-error
import openvino # pylint: disable=import-error
if silent:
return True
@@ -46,15 +55,10 @@ def import_core_modules(silent: bool, path_to_module: str):
print("\t- {}: \t{}".format("Inference Engine found in", os.path.dirname(openvino.__file__)))
print("{}: \t{}".format("Inference Engine version", ie_version))
print("{}: \t {}".format("Model Optimizer version", mo_version))
print("{}: \t{}".format("Model Optimizer version", mo_version))
versions_mismatch = False
# MO and IE version have a small difference in the beginning of version because
# IE version also includes API version. For example:
# Inference Engine version: 2.1.custom_HEAD_4c8eae0ee2d403f8f5ae15b2c9ad19cfa5a9e1f9
# Model Optimizer version: custom_HEAD_4c8eae0ee2d403f8f5ae15b2c9ad19cfa5a9e1f9
# So to match this versions we skip IE API version.
if not re.match(r"^([0-9]+).([0-9]+).{}$".format(mo_version), ie_version):
if mo_version != ie_version:
versions_mismatch = True
extracted_mo_release_version = v.extract_release_version(mo_version)
mo_is_custom = extracted_mo_release_version == (None, None)
+97 -1
View File
@@ -6,7 +6,6 @@ import ast
import logging as log
import os
import re
import sys
from collections import OrderedDict
from itertools import zip_longest
@@ -254,6 +253,14 @@ def get_common_cli_parser(parser: argparse.ArgumentParser = None):
'and biases are quantized to FP16.',
choices=["FP16", "FP32", "half", "float"],
default='float')
common_group.add_argument('--transform',
help='Apply additional transformations. ' +
'Usage: "--transform transformation_name1[args],transformation_name2..." ' +
'where [args] is key=value pairs separated by semicolon. ' +
'Examples: "--transform LowLatency" or ' +
' "--transform LowLatency[num_iterations=2]" ' +
'Available transformations: "LowLatency"',
default="")
common_group.add_argument('--disable_fusing',
help='Turn off fusing of linear operations to Convolution',
action=DeprecatedStoreTrue)
@@ -1127,6 +1134,95 @@ def get_absolute_path(path_to_file: str) -> str:
return file_path
def isfloat(value):
try:
float(value)
return True
except ValueError:
return False
def convert_string_to_real_type(value: str):
values = value.split(',')
for i in range(len(values)):
value = values[i]
if value.isdigit():
values[i] = int(value)
elif isfloat(value):
values[i] = float(value)
return values[0] if len(values) == 1 else values
def parse_transform(transform: str) -> list:
transforms = []
if len(transform) == 0:
return transforms
all_transforms = re.findall(r"([a-zA-Z0-9]+)(\[([^\]]+)\])*(,|$)", transform)
# Check that all characters were matched otherwise transform key value is invalid
key_len = len(transform)
for transform in all_transforms:
# In regexp we have 4 groups where 1st group - transformation_name,
# 2nd group - [args],
# 3rd group - args, <-- nested group
# 4th group - EOL
# And to check that regexp matched all string we decrease total length by the length of matched groups (1,2,4)
# In case if no arguments were given to transformation then 2nd and 3rd groups will be empty.
if len(transform) != 4:
raise Error("Unexpected transform key structure: {}".format(transform))
key_len -= len(transform[0]) + len(transform[1]) + len(transform[3])
if key_len != 0:
raise Error("Unexpected transform key structure: {}".format(transform))
for transform in all_transforms:
name = transform[0]
args = transform[2]
args_dict = {}
if len(args) != 0:
for arg in args.split(';'):
m = re.match(r"^([_a-zA-Z]+)=(.+)$", arg)
if not m:
raise Error("Unrecognized attributes for transform key: {}".format(transform))
args_dict[m.group(1)] = convert_string_to_real_type(m.group(2))
transforms.append((name, args_dict))
return transforms
def check_available_transforms(transforms: list, ie_is_available: bool):
"""
This function check that transformations specified by user are available.
:param transforms: list of user specified transformations
:param ie_is_available: True if IE Python API is available and False if it is not
:return: raises an Error if IE or transformation is not available
"""
if not ie_is_available and len(transforms) != 0:
raise Error('Can not apply {} transformations due to missing Inference Engine Python API'.format(
','.join([name for name, _ in transforms])))
from mo.back.offline_transformations import get_available_transformations
available_transforms = get_available_transformations()
missing_transformations = []
for name, _ in transforms:
if name not in available_transforms.keys():
missing_transformations.append(name)
if len(missing_transformations) != 0:
raise Error('Following transformations ({}) are not available. '
'List with available transformations ({})'.format(','.join(missing_transformations),
','.join(available_transforms.keys())))
return True
def check_positive(value):
try:
int_value = int(value)
+3 -1
View File
@@ -65,7 +65,9 @@ def get_simplified_ie_version(env=dict(), version=None):
version = subprocess.check_output([sys.executable, os.path.join(os.path.dirname(__file__), "ie_version.py")], timeout=2, env=env).strip().decode()
except:
return "ie not found"
# To support legacy IE versions
m = re.match(r"^([0-9]+).([0-9]+).(.*)", version)
if m and len(m.groups()) == 3:
return simplify_version(m.group(3))
return "custom"
return simplify_version(version)
+2 -11
View File
@@ -3,16 +3,7 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.versions_checker import check_python_version
if __name__ == "__main__":
ret_code = check_python_version()
if ret_code:
sys.exit(ret_code)
from mo.main import main
from mo.utils.cli_parser import get_caffe_cli_parser
sys.exit(main(get_caffe_cli_parser(), 'caffe'))
from mo.subprocess_main import subprocess_main
subprocess_main(framework='caffe')
+2 -11
View File
@@ -3,16 +3,7 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.versions_checker import check_python_version
if __name__ == "__main__":
ret_code = check_python_version()
if ret_code:
sys.exit(ret_code)
from mo.main import main
from mo.utils.cli_parser import get_kaldi_cli_parser
sys.exit(main(get_kaldi_cli_parser(), 'kaldi'))
from mo.subprocess_main import subprocess_main
subprocess_main(framework='kaldi')
+2 -11
View File
@@ -3,16 +3,7 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.versions_checker import check_python_version
if __name__ == "__main__":
ret_code = check_python_version()
if ret_code:
sys.exit(ret_code)
from mo.main import main
from mo.utils.cli_parser import get_mxnet_cli_parser
sys.exit(main(get_mxnet_cli_parser(), 'mxnet'))
from mo.subprocess_main import subprocess_main
subprocess_main(framework='mxnet')
+2 -11
View File
@@ -3,16 +3,7 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.versions_checker import check_python_version
if __name__ == "__main__":
ret_code = check_python_version()
if ret_code:
sys.exit(ret_code)
from mo.main import main
from mo.utils.cli_parser import get_onnx_cli_parser
sys.exit(main(get_onnx_cli_parser(), 'onnx'))
from mo.subprocess_main import subprocess_main
subprocess_main(framework='onnx')
+2 -11
View File
@@ -3,16 +3,7 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from mo.utils.versions_checker import check_python_version
if __name__ == "__main__":
ret_code = check_python_version()
if ret_code:
sys.exit(ret_code)
from mo.main import main
from mo.utils.cli_parser import get_tf_cli_parser
sys.exit(main(get_tf_cli_parser(), 'tf'))
from mo.subprocess_main import subprocess_main
subprocess_main(framework='tf')
@@ -14,7 +14,7 @@ import numpy.testing as npt
from mo.utils.cli_parser import get_placeholder_shapes, get_tuple_values, get_mean_scale_dictionary, get_model_name, \
parse_tuple_pairs, check_positive, writable_dir, readable_dirs, \
readable_file, get_freeze_placeholder_values
readable_file, get_freeze_placeholder_values, parse_transform, check_available_transforms
from mo.utils.error import Error
@@ -898,3 +898,71 @@ class PathCheckerFunctions(unittest.TestCase):
def test_non_readable_file(self):
with self.assertRaises(Error) as cm:
readable_file(__class__.NOT_EXISTING_FILE)
class TransformChecker(unittest.TestCase):
def test_empty(self):
self.assertEqual(parse_transform(""), [])
def test_single_pass(self):
self.assertEqual(parse_transform("LowLatency"), [("LowLatency", {})])
def test_single_pass_with_args(self):
self.assertEqual(parse_transform("LowLatency[num_iterations=2]"),
[("LowLatency", {"num_iterations": 2})])
def test_single_pass_with_multiple_args(self):
self.assertEqual(parse_transform("LowLatency[num_iterations=2;dummy_attr=3.14]"),
[("LowLatency", {"num_iterations": 2, "dummy_attr": 3.14})])
def test_multiple_passes_with_args(self):
self.assertEqual(parse_transform("LowLatency[num_iterations=2],DummyPass[type=ReLU]"),
[("LowLatency", {"num_iterations": 2}),
("DummyPass", {"type": "ReLU"})])
def test_multiple_passes_with_args2(self):
self.assertEqual(parse_transform("LowLatency[num_iterations=2,3,4.15],DummyPass1,DummyPass2[types=ReLU,PReLU;values=1,2,3]"),
[("LowLatency", {"num_iterations": [2,3,4.15]}),
("DummyPass1", {}),
("DummyPass2", {"types": ["ReLU", "PReLU"], "values": [1,2,3]})])
def test_multiple_passes_no_args(self):
self.assertEqual(parse_transform("DummyPass,LowLatency2"),
[("DummyPass", {}), ("LowLatency2", {})])
def test_single_pass_neg(self):
self.assertRaises(Error, parse_transform, "LowLatency!")
def test_multiple_passes_neg(self):
self.assertRaises(Error, parse_transform, "LowLatency;DummyPass")
def test_single_pass_with_args_neg1(self):
self.assertRaises(Error, parse_transform, "LowLatency[=2]")
def test_single_pass_with_args_neg2(self):
self.assertRaises(Error, parse_transform, "LowLatency[key=]")
def test_single_pass_with_args_neg3(self):
self.assertRaises(Error, parse_transform, "LowLatency[]")
def test_single_pass_with_args_neg4(self):
self.assertRaises(Error, parse_transform, "LowLatency[key=value;]")
def test_single_pass_with_args_neg5(self):
self.assertRaises(Error, parse_transform, "LowLatency[value]")
def test_single_pass_with_args_neg6(self):
self.assertRaises(Error, parse_transform, "LowLatency[key=value")
@patch("mo.back.offline_transformations.get_available_transformations")
def test_check_low_latency_is_available(self, available_transformations):
available_transformations.return_value = {"LowLatency": None}
try:
check_available_transforms([("LowLatency" ,"")], True)
except Error as e:
self.assertTrue(False, "Exception \"{}\" is unexpected".format(e))
@patch("mo.back.offline_transformations.get_available_transformations")
def test_check_dummy_pass_is_available(self, available_transformations):
available_transformations.return_value = {"LowLatency": None}
self.assertRaises(Error, check_available_transforms, [("DummyPass", "")], True)
@@ -52,11 +52,14 @@ class TestingVersion(unittest.TestCase):
mock_open.return_value.__enter__ = mock_open
self.assertEqual(get_simplified_mo_version(), "custom")
def test_simplify_ie_version_release(self):
def test_simplify_ie_version_release_legacy(self):
self.assertEqual(get_simplified_ie_version(version="2.1.custom_releases/2021/3_4c8eae"), "2021.3")
def test_simplify_ie_version_release_neg(self):
self.assertEqual(get_simplified_ie_version(version="custom_releases/2021/3_4c8eae"), "custom")
def test_simplify_ie_version_release(self):
self.assertEqual(get_simplified_ie_version(version="custom_releases/2021/3_4c8eae"), "2021.3")
def test_simplify_ie_version_custom_legacy(self):
self.assertEqual(get_simplified_ie_version(version="2.1.custom_my/branch/3_4c8eae"), "custom")
def test_simplify_ie_version_custom(self):
self.assertEqual(get_simplified_ie_version(version="2.1.custom_my/branch/3_4c8eae"), "custom")
self.assertEqual(get_simplified_ie_version(version="custom_my/branch/3_4c8eae"), "custom")