def _resolve_duplicates()

in onnxconverter_common/topology.py [0:0]


    def _resolve_duplicates(self):
        '''
        Merge variables connected by identity operator to reduce the number of redundant variables
        '''
        self._initialize_graph_status_for_traversing()

        # Traverse the graph from roots to leaves
        for operator in self.topological_operator_iterator():
            if operator.type != 'identity':
                continue

            if any(variable.is_root for variable in operator.inputs) and \
                    any(variable.is_leaf for variable in operator.outputs):
                continue

            # Replace the output variable with the input variable everywhere
            original = operator.inputs[0]
            duplicate = operator.outputs[0]
            for another_scope in self.scopes:
                for another_operator in another_scope.operators.values():
                    for i in range(len(another_operator.inputs)):
                        if another_operator.inputs[i].onnx_name != duplicate.onnx_name:
                            continue
                        another_operator.inputs[i] = original

            # When original variable's documentation string or denotation is empty but duplicate's is not, we
            # copy that field to the original variable to avoid information loss.
            if not original.type.doc_string and duplicate.type.doc_string:
                original.type.doc_string = duplicate.type.doc_string

            if isinstance(original.type, TensorType) and isinstance(duplicate.type, TensorType):
                if not original.type.denotation and duplicate.type.denotation:
                    original.type.denotation = duplicate.type.denotation
                if not original.type.channel_denotations:
                    original.type.channel_denotations = duplicate.type.channel_denotations
                elif duplicate.type.channel_denotations:
                    # Merge the channel denotations if available in both the original and the duplicate
                    for i in range(len(original.type.channel_denotations)):
                        if original.type.channel_denotations[i]:
                            continue
                        original.type.channel_denotations[i] = duplicate.type.channel_denotations[i]
                # Sometime, shapes of duplicates are different.
                # We try to replace the original variable's unknown dimensions...
                # ...as many as possible because we will get rid of the duplicate.
                if len(original.type.shape) == len(duplicate.type.shape):
                    for i in range(len(original.type.shape)):
                        if original.type.shape[i] != 'None':
                            continue
                        original.type.shape[i] = duplicate.type.shape[i]

            # Because we're iterating through the topology, we cannot delete any operator or variable. Otherwise,
            # the traversing function may be broken. We will delete those abandoned ones later.
            duplicate.is_abandoned = True
            operator.is_abandoned = True

        for scope in self.scopes:
            # Find out who is going to be abandoned
            abandoned_operator_names = set(onnx_name for onnx_name, operator in scope.operators.items()
                                           if operator.is_abandoned)
            abandoned_variable_names = set(onnx_name for onnx_name, variable in scope.variables.items()
                                           if variable.is_abandoned)

            # Remove abandoned operators
            for name in abandoned_operator_names:
                scope.delete_local_operator(name)

            # Remove abandoned variables
            for name in abandoned_variable_names:
                scope.delete_local_variable(name)