#########################################################
# Copyright (C) 2020 SiMa Technologies, Inc.
#
# This material is SiMa proprietary and confidential.
#
# This material may not be copied or distributed without
# the express prior written permission of SiMa.
#
# All rights reserved.
#########################################################
# Code owner: Joey Chou
#########################################################
from collections import OrderedDict
from typing import List, Union, Dict, Any, Optional
from dataclasses import dataclass
from afe.backends import BackendIR, Backend
from afe.ir import attributes as afe_attrs
from afe.ir import operations as afe_op
from afe.ir.defines import Status, NodeName, InputName, data_value_elements, LayerStats
from afe.ir.operations import PlaceholderOp
from afe.ir.sima_ir import SiMaIR
from afe.ir.tensor_type import NodeType, ScalarType
@dataclass
[docs]
class AwesomeNode:
"""
An awesome node is responsible for storing attributes and operations used by the AwesomeNet when
executing the network
:param name: Name of the node
:param input_names: Parameter names of this AwesomeNode. These represent the inputs expected by
the code inside this node.
:param input_node_names: Argument names of this AwesomeNode. These represent the inputs supplied
by the net that contains this node. Data for each argument gets forwarded to the corresponding
parameter.
:param ir: Union[SiMaIR, "afe.ir.net.AwesomeNet", "afe.backends.BackendIR"]. The IR can be:
* SiMaIR - A frontend IR that contains single operation or ExternalOp
* AwesomeNet - A frontend IR that contains a sub-graph that can be supported by SiMa's backends
* BackendIR - A backend IR that can be compiled, or already compiled by certain backend
:param _status: Status. Node status. Default is Status.RELAY
:param _layer_stats: Layer statistics. For each MLA node, quantization error is calculated,
that information is than forwarded to .sima.json file, and it can be viewed in Netron.
"""
[docs]
ir: Union[SiMaIR, "afe.ir.net.AwesomeNet", "afe.backends.BackendIR"]
_status: Status = Status.RELAY
_layer_stats: Optional[LayerStats] = None
@property
[docs]
def status(self) -> Status:
return self._status
@status.setter
def status(self, status: Status):
self._status = status
[docs]
def get_type(self) -> NodeType:
import afe.ir.net
import afe.backends
if isinstance(self.ir, (SiMaIR, afe.backends.BackendIR)):
return self.ir.get_type()
elif isinstance(self.ir, afe.ir.net.AwesomeNet):
return _make_net_type(self.input_names, self.ir)
else:
raise TypeError("Unrecognized type of data in AwesomeNode")
[docs]
def set_batch_size(self, batch_size: int):
"""
Modifies AwesomeNode's internal parameters to accommodate for a given batch size.
:param batch_size: Integer value representing the batch size of the inputs to the AwesomeNet.
"""
self.ir.set_batch_size(batch_size)
[docs]
def update_layer_stats(self, layer_stats: LayerStats):
self._layer_stats = layer_stats
@property
[docs]
def layer_stats(self) -> LayerStats:
return self._layer_stats
def _make_net_type(input_names: List[NodeName], net: "afe.ir.net.AwesomeNet") -> NodeType:
"""
Make the type of an AwesomeNode that contains an AwesomeNet.
The parameter names are taken from the AwesomeNode. Their associated types and
the return type are taken from the AwesomeNet.
:param input_names: The AwesomeNode's parameters. These determine the parameters
in the node type.
:param net: The AwesomeNet that implements the node.
:return: The type of the node.
"""
assert len(input_names) == len(net.input_node_names)
parameter_types = {name: net.nodes[input_name].get_type().output
for name, input_name in zip(input_names, net.input_node_names)}
return_type = net.nodes[net.output_node_name].get_type().output
return NodeType(parameter_types, return_type)
[docs]
def node_is_sima_ir(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains a SiMa IR
:param node: AwesomeNode
"""
return isinstance(node.ir, SiMaIR)
[docs]
def node_is_backend_ir(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains a SiMa IR
:param node: AwesomeNode
"""
return isinstance(node.ir, BackendIR)
[docs]
def node_is_constant(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains a Constant SiMaIR
:param node: AwesomeNode
"""
return node_is_sima_ir(node) and isinstance(node.ir.operation, afe_op.ConstantOp)
[docs]
def node_is_placeholder(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains a Placeholder SiMaIR
:param node: AwesomeNode
"""
return node_is_sima_ir(node) and isinstance(node.ir.operation, PlaceholderOp)
[docs]
def node_is_awesomenet(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains a AwesomeNet
:param node: AwesomeNode
"""
return node.ir.__class__.__name__ == "AwesomeNet"
[docs]
def node_is_external(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains a External Graph
:param node: AwesomeNode
"""
return node_is_sima_ir(node) and isinstance(node.ir.attrs, afe_attrs.ExternalAttrs)
[docs]
def node_is_ev(node: AwesomeNode) -> bool:
"""
Return True if the AwesomeNode is an EV node.
:param node: AwesomeNode
"""
return node_is_sima_ir(node) and node.ir.backend == Backend.EV
[docs]
def node_is_relu(node: AwesomeNode) -> bool:
return (node_is_sima_ir(node)
and isinstance(node.ir.operation, (afe_op.ReluOp, afe_op.PReluOp, afe_op.LeakyReluCompositeOp)))
[docs]
def node_is_mean(node: AwesomeNode) -> bool:
return node_is_sima_ir(node) and isinstance(node.ir.operation, afe_op.MeanOp)
[docs]
def node_is_upsampling(node: AwesomeNode) -> bool:
return node_is_sima_ir(node) and isinstance(node.ir.operation, (afe_op.UpsamplingOp, afe_op.ImageResize2DOp))
[docs]
def node_is_subgraph(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains a sub-graph.
Parameters
----------
:param node: AwesomeNode
Return
------
:return: bool
"""
return node_is_awesomenet(node) or node_is_external(node)
[docs]
def node_is_tuple(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains a Tuple.
Parameters
----------
:param node: AwesomeNode
Return
------
:return: bool
"""
return node_is_sima_ir(node) and isinstance(node.ir.attrs, afe_attrs.TupleAttrs)
[docs]
def node_is_tuple_get_item(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains a TupleGetItem.
Parameters
----------
:param node: AwesomeNode
Return
------
:return: bool
"""
return node_is_sima_ir(node) and isinstance(node.ir.attrs, afe_attrs.TupleGetItemAttrs)
[docs]
def node_is_unpack(node: AwesomeNode) -> bool:
"""
Return True is the AwesomeNode contains an Unpack transform.
Parameters
----------
:param node: AwesomeNode
Return
------
:return: bool
"""
return node_is_sima_ir(node) and isinstance(node.ir.attrs, afe_attrs.UnpackTransformAttrs)
[docs]
def node_is_fp32_node(node: AwesomeNode) -> bool:
"""
Return True if AwesomeNode's output type is floating point.
:param node: AwesomeNode to be analyzed.
:return: bool. True if AwesomeNode's output type is fp32, otherwise False.
"""
output_type = node.get_type().output
return all([el.scalar == ScalarType.float32 for el in data_value_elements(output_type)])
[docs]
def node_is_pool_node(node: AwesomeNode) -> bool:
"""
Return True if the AwesomeNode contains Pool operator.
:param node: AwesomeNode to be analyzed.
:return: bool. True if the AwesomeNode's operator is Pooling operator (MaxPool, AvgPool).
"""
return node_is_sima_ir(node) and isinstance(node.ir.operation, (afe_op.AvgPool2DOp, afe_op.MaxPool2DOp))
[docs]
def node_is_requantization_node(node: AwesomeNode) -> bool:
"""
Return True if the AwesomeNode contains Requantize operator.
:param node: AwesomeNode to be analyzed.
:return: bool. True if the AwesomeNode's operator is Requantize operator.
"""
return node_is_sima_ir(node) and isinstance(node.ir.operation, afe_op.RequantizeOp)
[docs]
def node_uses_observer(node: AwesomeNode) -> bool:
"""
Return True if node uses calibration information during quantization.
:param node: AwesomeNode to be analyzed.
:return: bool. True if AwesomeNode uses calibration information during quantization.
Otherwise, quantization information is obtained using quantization from its input
node(s).
"""
# Currently, nodes that do not use calibration information and thus do not contain
# NodeObservers are:
# - nodes that bypass input to output
# - external nodes
# - pool nodes
# TODO: Extend this list?
return not (
node.ir.should_bypass_input_to_output() or
node_is_external(node) or
node_is_pool_node(node) or
node_is_mean(node) or
node_is_relu(node) or
node_is_upsampling(node)
)