in tensorboard/plugins/graph/tf_graph_common/hierarchy.ts [651:755]
function addNodes(h: Hierarchy, graph: SlimGraph) {
// Maps the op of a node to names of nodes that have the op. Used to populate
// the libraryFunctions field of the hierarchy.
const opToNode = {};
_.each(graph.nodes, (node, nodeName) => {
let path = getHierarchicalPath(node.name);
let parent: Metanode = h.root;
parent.depth = Math.max(path.length, parent.depth);
// Track which nodes are associated with which ops.
if (!opToNode[node.op]) {
opToNode[node.op] = [];
}
opToNode[node.op].push(node);
// Create parent metanodes for each depth. For example if the node name
// is 'a/b/c', then create metanodes 'a' and 'a/b', where 'a/b' is a child
// of a.
for (let i = 0; i < path.length; i++) {
parent.depth = Math.max(parent.depth, path.length - i);
parent.cardinality += node.cardinality;
parent.opHistogram[node.op] = (parent.opHistogram[node.op] || 0) + 1;
if (node.device != null) {
parent.deviceHistogram[node.device] =
(parent.deviceHistogram[node.device] || 0) + 1;
}
if (node.xlaCluster != null) {
parent.xlaClusterHistogram[node.xlaCluster] =
(parent.xlaClusterHistogram[node.xlaCluster] || 0) + 1;
}
// Increment parents appropriate compatibility count
if (node.compatible) {
parent.compatibilityHistogram.compatible =
(parent.compatibilityHistogram.compatible || 0) + 1;
} else {
parent.compatibilityHistogram.incompatible =
(parent.compatibilityHistogram.incompatible || 0) + 1;
}
// Increment capability counts for in and out embeddings
_.each(node.inEmbeddings, (inNode) => {
if (inNode.compatible) {
parent.compatibilityHistogram.compatible =
(parent.compatibilityHistogram.compatible || 0) + 1;
} else {
parent.compatibilityHistogram.incompatible =
(parent.compatibilityHistogram.incompatible || 0) + 1;
}
});
_.each(node.outEmbeddings, (outNode) => {
if (outNode.compatible) {
parent.compatibilityHistogram.compatible =
(parent.compatibilityHistogram.compatible || 0) + 1;
} else {
parent.compatibilityHistogram.incompatible =
(parent.compatibilityHistogram.incompatible || 0) + 1;
}
});
if (i === path.length - 1) {
break;
}
let name = path[i];
let child = <Metanode>h.node(name);
if (!child) {
child = createMetanode(name, h.graphOptions);
child.parentNode = parent;
h.setNode(name, child);
parent.metagraph.setNode(name, child);
if (
name.indexOf(tf_graph.FUNCTION_LIBRARY_NODE_PREFIX) === 0 &&
parent.name === tf_graph.ROOT_NAME
) {
// This metanode represents a function in the Library. We later copy
// its contents to dynamically inject function data into the graph
// when the subhierarchy of a metanode is built (upon its expansion).
const functionName = name.substring(
tf_graph.FUNCTION_LIBRARY_NODE_PREFIX.length
);
// For now, remember the metanode that represents the function with
// this name.
if (!opToNode[functionName]) {
opToNode[functionName] = [];
}
h.libraryFunctions[functionName] = {
node: child,
usages: opToNode[functionName],
};
child.associatedFunction = functionName;
}
}
parent = child;
}
// Assuming node name is 'a/b/c', assign the OpNode as a child of the
// metanode 'a/b'.
h.setNode(node.name, node);
node.parentNode = parent;
parent.metagraph.setNode(node.name, node);
// Add each of the in-embeddings and out-embeddings in the hierarchy.
_.each(node.inEmbeddings, function (embedding) {
h.setNode(embedding.name, embedding);
embedding.parentNode = node;
});
_.each(node.outEmbeddings, function (embedding) {
h.setNode(embedding.name, embedding);
embedding.parentNode = node;
});
});
}