* Written MO classes for DFT and IDFT operations. * Added class to read TF (I)FFT operations. * Written extractors for TF operations FFT, FFT2D, FFT3D, IFFT, IFFT2D, IFFT3D. * Written MO Roll operation and TF Roll operation extractor. * Started to write needed transformations. * Written transformation StridedSlices + Complex + Roll + (i)FFTxD + Roll + (Imag, Real) + Pack -> Roll + (I)DFT + Roll. * Written transformation for Complex + ComplexAbs. * Written correction of axes of Roll. * Small fix. * Small fix. * Some fixes. * Some changes. * Now TF Roll is read as TFRoll. Written inserting Transposes before and after (I)DFT. * Small fix. * Written tests for the transformation TFRollToRoll. * Added comments to some transformations. * Deleted redundant import. * Written tests for the transformation TransposeDFT. * Fixes in MO IR Reader to read/write (I)DFT. * Fixes in the list of supported TF layers. * Started to write tests for SSliceComplexRolledFFTPackBlockReplacement transformation. * Written tests for the MO transformation SSliceComplexRolledFFTPackBlockReplacement. * Written tests for the MO transformation ComplexAbs. * Tests for transformations were moved into unit_tests directory. * All extractors for (I)FFTxD are in one file now. * Deleted redundant transformations. * Fixed extractor for TF Roll: now this operation is read as MO Roll. * Added comments to TFFFT operation. * The method insert_transpose of classes TransposeDFT and LayoutChangeForGatherND was moved into the separate function in the file model-optimizer/extensions/middle/InsertLayoutPropagationTransposes.py. * Fixed comment for the transformation TransposeDFT. * Small fix. * Some fixes. * Deleted shape infer function for the operation TFFFT. Sorted imports in complex_abs.py. * Small fixes. * Deleted redundant import. * Fixes in some asserts. * Small fix. * Added names for created nodes in the transformation ComplexAbs. * Added comments to the method canonicalize_axes. * The transformation SSliceComplexRolledFFTPackBlockReplacement was split into the sequence of transformations SSliceComplexRollReplacement -> RollRealImagPackReplacement -> TFFFTToDFT. * Written tests for the transformation SSliceComplexRollReplacement. * Written tests for the transformation RollRealImagPackReplacement. * Written tests for the transformation TFFFTToDFT. * Deleted commented code. * Fixed types of constants in the transformation ComplexAbs. * Written tests for canonicalization of signal_size value. * Deleted 'Replacement' from names of files and classes. * Used comarison of ids, not names. * replace_sub_graph was replaced with find_and_replace_pattern. * Now the transformation RollRealImagPack is executed before running transformation model-optimizer/extensions/front/Pack.py. * The body of the function create_dft_from_tffft is a part of the transformation TFFFTToDFT body now. * Now method correct_roll_axes of classes RollRealImagPack and SSliceComplexRoll is moved to the function in mo/front/tf/graph_utils.py. * Small changes. * Added comment before mark_input_as_in_correct_layout(roll, 2). * Now the functions correct_roll_axes generates sub-graph in the input port 2 of Roll. * Corrected tests for the transformation SSliceComplexRoll. * Corrected tests for the transformation RollRealImagPack. * Deleted commented code. * Some renaming. * Added decomposition of the separate operation ComplexAbs (without Complex before it). * Added comment to the transformation ComplexAbsAfterComplex. * Optimized imports for the transformation TFFFTToDFT. * The transformation SSliceComplexRoll was split into the sequence SSliceComplex -> CorrectRollAxes and disabled. * Written tests for the transformation ComplexAbs. * Written tests for the transformation SSliceComplex. * Written tests for the transformation CorrectRollAxes. * Deleted the transformation SSliceComplexRoll. * Deleted renaming nodes. * Fixed comment. * Small fixes. * Small fix. * The attribute need_correction was renamed as input_rank_changed. * Small fixes. * Deleted commented code. * Now we iterate over all complex_node.out_port(0).get_connection().get_destinations() input ports and mark the corresponding nodes with the marker attribute. * Added the attribute 'in_ports_count' into the class FFTBase. * Tests for the transformation TransposeDFT were rewritten using helper functions. * Now the transformation RollRealImagPack uses existing Roll node instead of creating new one. * Small fixes. * Fix in the documentation. * Written class to read MxNet (I)FFT operations. Written corresponding extractors. * Corrected shape infer function for MXFFT operation. Written transformation to convert MXFFT to (I)DFT. * Fixed shape infer function. * Fixed the conversion MXFFT to (I)DFT. * Written tests for the transformation MXFFTToDFT. * The function correct_roll_axes was replaced with more generic function add_constant_to_negative_values. * Fixes in classes TFFFT, FFTBase, DFT, IDFT, MXFFT. * Added asserts in constructors of operations TFFFT and MXFFT. * Refactored transformation MXFFTToDFT: conversion of DFT and IDFT were moved into separated functions. * Moved some commented code. * Fixed BOM file. * Written function convert_ifft_to_dft. * Started to rewrite tests for MXFFTToDFT transformations, in the case is_inverse=False. * Small fixes. * Fixes in the transformation RollRealImagPack. * Renaming tests class for the transformation SSliceComplex. * Fixes in the function compare_graphs. Now we get all output nodes of op node, and these output nodes are sorted by names. * Fixed tests for the transformation MXFFTToDFT. * Fix in the transformation ThresholdedReluDecomposition: added disconnect for trelu input port. * Fixes in test for the transformation TFSliceToSlice. * Small fix in the transformation ObjectDetectionAPIPreprocessor2Replacement. * Small fix in comment. * Optimized imports. * Used remove_node in the transformation ThresholdedReluDecomposition and remove_nodes_from in the transformation RollRealImagPack, instead of ports disconnection. * Deleted commented code. * Deleted test case test_slice_replacer_begin_with_2_inputs.
169 lines
8.5 KiB
Python
169 lines
8.5 KiB
Python
# Copyright (C) 2018-2021 Intel Corporation
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from collections import deque
|
|
from numbers import Number
|
|
|
|
import numpy as np
|
|
|
|
from mo.graph.graph import Graph, Node
|
|
|
|
|
|
def compare_graphs(graph: Graph, graph_ref: Graph, last_node: str, last_node_ref=None, check_op_attrs=False):
|
|
from mo.utils.ir_engine.ir_engine import IREngine
|
|
stderr = []
|
|
if last_node_ref is None:
|
|
last_node_ref = last_node
|
|
|
|
if 'statistics' in graph.graph and 'statistics' in graph_ref.graph:
|
|
assert graph.graph['statistics'] == graph_ref.graph['statistics'], "int8 statistics comparison failed"
|
|
|
|
q = deque([last_node])
|
|
q_ref = deque([last_node_ref])
|
|
|
|
checked_nodes = []
|
|
checked_nodes_ref = []
|
|
|
|
while len(q_ref) != 0:
|
|
if len(q) == 0:
|
|
stderr.append('Graphs have different number of nodes')
|
|
return (False, stderr)
|
|
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()):
|
|
stderr.append('Current node "{}" and reference node "{}" have different amount of output nodes: {} vs {}'.\
|
|
format(node.id, node_ref.id, len(node.out_nodes()), len(node_ref.out_nodes())))
|
|
continue
|
|
|
|
# Check that nodes has same amount of input nodes
|
|
if len(node_ref.in_nodes()) != len(node.in_nodes()):
|
|
stderr.append('Current node "{}" and reference node "{}" have different amount of input nodes: {} vs {}'.\
|
|
format(node.id, node_ref.id, len(node.in_nodes()), len(node_ref.in_nodes())))
|
|
continue
|
|
|
|
# Check that nodes has same 'kind'
|
|
if node_ref.kind != node.kind:
|
|
stderr.append('Current node "{}" and reference node "{}" have different kind parameter'.\
|
|
format(node.id, node_ref.id))
|
|
return (False, stderr)
|
|
|
|
# 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'):
|
|
stderr.append('Current node "{}" and reference node "{}" have 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:
|
|
cur_node_type = node.type if node.has_valid("type") else None
|
|
ref_node_type = node_ref.type if node_ref.has_valid("type") else None
|
|
for attr in graph_ref.node[node_ref.id]:
|
|
if graph_ref.node[node_ref.id][attr] is None or attr in ['name', 'id', '_in_ports', '_out_ports',
|
|
'infer', 'IE', 'biases', 'weights', 'custom', 'offset']:
|
|
continue
|
|
if attr not in graph.node[node.id]:
|
|
stderr.append('Current node "{}" with type {} has missing attribute {}'.format(node.id, cur_node_type, attr))
|
|
continue
|
|
|
|
if attr == 'value':
|
|
if not values_are_equal(node.value, node_ref.value):
|
|
stderr.append('Current node "{}" with type {} and reference node "{}" with type have different values '
|
|
'\n{} \nand \n{}'.format(node.id, cur_node_type, node_ref.id, ref_node_type, node.value, node_ref.value))
|
|
continue
|
|
|
|
def err_format_string():
|
|
return 'Current node "{}" with type {} and reference node "{}" with type {} have different attr "{}" : ' \
|
|
'{} and {}'.format(node.id, cur_node_type, node_ref.id, ref_node_type, attr,
|
|
graph.node[node.id][attr],
|
|
graph_ref.node[node_ref.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]):
|
|
stderr.append(err_format_string())
|
|
elif isinstance(graph.node[node.id][attr], Number):
|
|
eps = 5e-2 if node.has('precision') and node['precision'] == 'FP16' else 1e-4
|
|
if abs(graph.node[node.id][attr] - graph_ref.node[node_ref.id][attr]) > eps:
|
|
stderr.append(err_format_string())
|
|
elif isinstance(graph.node[node.id][attr], IREngine):
|
|
resp, err_log = graph.node[node.id][attr].compare(graph_ref.node[node_ref.id][attr])
|
|
if not resp:
|
|
stderr.extend(err_log)
|
|
elif isinstance(graph.node[node.id][attr], np.ndarray):
|
|
if not np.array_equal(graph.node[node.id][attr], graph_ref.node[node_ref.id][attr]):
|
|
stderr.append(err_format_string())
|
|
elif graph.node[node.id][attr] != graph_ref.node[node_ref.id][attr]:
|
|
stderr.append(err_format_string())
|
|
else:
|
|
if node_ref.has_valid('shape') and not node.has_valid('shape'):
|
|
stderr.append('{} has None shape'.format(node.id))
|
|
if node_ref.has_valid('value') and not node.has_valid('value'):
|
|
stderr.append('{} 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):
|
|
stderr.append('Current node "{}" and reference node "{}" have different shapes {} and {}'.\
|
|
format(node.id, node_ref.id, node.shape, node_ref.shape))
|
|
continue
|
|
|
|
if node_ref.has_valid('value') and node_ref.value is not None and \
|
|
not values_are_equal(node.value, node_ref.value):
|
|
stderr.append('Current node "{}" and reference node "{}" have 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:
|
|
stderr.append('Current node "{}" and reference node "{}" have different ports'.format(node.id, node_ref.id))
|
|
return (False, stderr)
|
|
|
|
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)
|
|
|
|
if node.kind == 'op':
|
|
out_nodes = sorted_by_name([Node(graph, v) for v, _ in node.get_outputs()])
|
|
else:
|
|
out_nodes = sorted_by_name(node.out_nodes())
|
|
for out_node in out_nodes:
|
|
if out_node.id not in checked_nodes and out_node.id not in q:
|
|
q.append(out_node.id)
|
|
|
|
if node_ref.kind == 'op':
|
|
out_nodes = sorted_by_name([Node(graph_ref, v) for v, _ in node_ref.get_outputs()])
|
|
else:
|
|
out_nodes = sorted_by_name(node_ref.out_nodes())
|
|
for out_node in out_nodes:
|
|
if out_node.id not in checked_nodes_ref and out_node.id not in q_ref:
|
|
q_ref.append(out_node.id)
|
|
|
|
return (False, stderr) if stderr else (True, [])
|
|
|
|
|
|
def sorted_by_name(nodes_list):
|
|
return sorted(nodes_list, key=lambda x: x.soft_get('name', x.id))
|
|
|
|
|
|
def values_are_equal(value, value_ref):
|
|
dtype = np.asarray(value).dtype
|
|
if dtype == 'uint8':
|
|
eps = 0
|
|
elif dtype == 'float16':
|
|
eps = 5e-2
|
|
else:
|
|
eps = 1e-4
|
|
return np.allclose(value_ref, value, rtol=eps, atol=eps)
|
|
|