[LEGACY] Graph Traversal and Modification¶
The code described here has been deprecated! Do not use it to avoid working with a legacy solution. It will be kept for some time to ensure backwards compatibility, but you should not use it in contemporary applications.
This guide describes a deprecated TensorFlow conversion method. The guide on the new and recommended method, using a new frontend, can be found in the Frontend Extensions article.
There are three APIs for a graph traversal and transformation used in the Model Optimizer:
1. The API provided with the
networkx Python library for the
networkx.MultiDiGraph class, which is the base class for
mo.graph.graph.Graph object. For example, the following methods belong to this API level:
other methods where
graphis a an instance of the
This is the lowest-level API. Avoid using it in the Model Optimizer transformations. For more details, refer to the Model Representation in Memory section.
2. The API built around the
mo.graph.graph.Node class. The
Node class is the primary class to work with graph nodes
and their attributes. Examples of such methods and functions are:
There are some “Node” class methods not recommended for use and some functions defined in the mo.graph.graph have been deprecated. For more details, refer to the
3. The high-level API called Model Optimizer Graph API, which uses
mo.graph.connection.Connection classes. For example, the following methods belong to this API level:
This is the recommended API for the Model Optimizer transformations and operations implementation.
The main benefit of using the Model Optimizer Graph API is that it hides some internal implementation details (the fact that the graph contains data nodes), provides API to perform safe and predictable graph manipulations, and adds operation semantic to the graph. This is achieved with introduction of concepts of ports and connections.
This article is dedicated to the Model Optimizer Graph API only and does not cover other two non-recommended APIs.
An operation semantic describes how many inputs and outputs the operation has. For example,
Parameter and Const operations have no
inputs and have one output, ReLU operation has one input and one output,
Split operation has 2 inputs and a variable number of outputs depending on the value of the
Each operation node in the graph (an instance of the
Node class) has 0 or more input and output ports (instances of
mo.graph.port.Port class). The
Port object has several attributes:
node- the instance of the
Nodeobject the port belongs to.
idx- the port number. Input and output ports are numbered independently, starting from
ReLU operation has one input port (with index
0) and one output port (with index
type - the type of the port. Could be equal to either
data - the object that should be used to get attributes of the corresponding data node. This object has methods
set_value() to get/set shape/value of the corresponding data node. For example,
in_port.data.get_shape() returns an input shape of a tensor connected to input port
in_port.type == 'in'),
out_port.data.get_value() returns a value of a tensor produced from output port
out_port.type == 'out').
None until the partial inference phase. For more information about model conversion phases, refer to the Model Conversion Pipeline. For information about partial inference phase, see the Partial Inference.
There are several methods of the
Node class to get the instance of a corresponding port:
out_port(x)to get the input/output port with number
out_ports()to get a dictionary, where key is a port number and the value is the corresponding input/output port.
out_ports_count of the
Op class instance define default number of input and output
ports to be created for the
Node. However, additional input/output ports can be added using methods
add_output_port(). Port also can be removed, using the
Port class is just an abstraction that works with edges incoming/outgoing to/from a specific
Node instance. For
example, output port with
idx = 1 corresponds to the outgoing edge of a node with an attribute
out = 1, the input
idx = 2 corresponds to the incoming edge of a node with an attribute
in = 2.
Consider the example of a graph part with 4 operation nodes “Op1”, “Op2”, “Op3”, and “Op4” and a number of data nodes depicted with light green boxes.
Operation nodes have input ports (yellow squares) and output ports (light purple squares). Input port may not be connected. For example, the input port 2 of node Op1 does not have incoming edge, while output port always has an associated data node (after the partial inference when the data nodes are added to the graph), which may have no consumers.
Ports can be used to traverse a graph. The method
get_source() of an input port returns an output port producing the
tensor consumed by the input port. It is important that the method works the same during front, middle and back phases of a
model conversion even though the graph structure changes (there are no data nodes in the graph during the front phase).
Let’s assume that there are 4 instances of
op1, op2, op3, and
op4 corresponding to nodes Op1, Op2,
Op3, and Op4, respectively. The result of
op4.in_port(1).get_source() is the
op1.out_port(1) of type
get_destination() of an output port returns the input port of the node consuming this tensor. If there are
multiple consumers of this tensor, the error is raised. The method
get_destinations() of an output port returns a
list of input ports consuming the tensor.
disconnect() removes a node incoming edge corresponding to the specific input port. The method removes
several edges if it is applied during the front phase for a node output port connected with multiple nodes.
port.connect(another_port) connects output port
port and input port
another_port. The method handles
situations when the graph contains data nodes (middle and back phases) and does not create an edge between two nodes
but also automatically creates data node or reuses existing data node. If the method is used during the front phase and
data nodes do not exist, the method creates edge and properly sets
out edge attributes.
For example, applying the following two methods to the graph above will result in the graph depicted below:
For a full list of available methods, refer to the
Node class implementation in the
Port class implementation in the
Connection is a concept introduced to easily and reliably perform graph modifications. Connection corresponds to a
link between a source output port with one or more destination input ports or a link between a destination input port
and source output port producing data. So each port is connected with one or more ports with help of a connection.
Model Optimizer uses the
mo.graph.connection.Connection class to represent a connection.
There is only one
get_connection() method of the
Port class to get the instance of the corresponding
object. If the port is not connected, the returned value is
For example, the
op3.out_port(0).get_connection() method returns a
Connection object encapsulating edges from node
Op3 to data node data_3_0 and two edges from data node data_3_0 to two ports of the node Op4.
Connection class provides methods to get source and destination(s) ports the connection corresponds to:
connection.get_source()- returns an output
Portobject producing the tensor.
connection.get_destinations()- returns a list of input
Portconsuming the data.
connection.get_destination()- returns a single input
Portconsuming the data. If there are multiple consumers, the exception is raised.
Connection class provides methods to modify a graph by changing a source or destination(s) of a connection. For
example, the function call
op3.out_port(0).get_connection().set_source(op1.out_port(0)) changes source port of edges
consuming data from port
op1.out_port(0). The transformed graph from the sample above is depicted
Another example is the
connection.set_destination(dest_port) method. It disconnects
dest_port and all input ports to which
the connection is currently connected and connects the connection source port to
Note that connection works seamlessly during front, middle, and back phases and hides the fact that the graph structure is different.
For a full list of available methods, refer to the
Connection class implementation in the