def flattenTrees(root, nodeSelector: Callable[[LNode], bool]):
    """
    Walk all nodes and discover trees of nodes (usually operators)
    and reduce them to single node with multiple outputs

    :attention: selected nodes has to have single output
                and has to be connected to nets with single driver
    """
    for ch in root.children:
        if ch.children:
            flattenTrees(ch, nodeSelector)

    # collect all nodes which can be potentialy reduced
    reducibleChildren = set()
    for ch in root.children:
        if nodeSelector(ch):
            reducibleChildren.add(ch)

    while reducibleChildren:
        # try to pick a node from random tree and search it's root
        _treeRoot = reducibleChildren.pop()
        reducibleChildren.add(_treeRoot)
        # we need to keep order of inputs, use preorder
        treeRoot = searchRootOfTree(reducibleChildren, _treeRoot)

        reducedNodes, inputEdges = collectNodesInTree(treeRoot, reducibleChildren)
        # if tree is big enoguh for reduction, reduce it to single node
        if len(reducedNodes) > 1:
            newName = reducedNodes[0].name
            newNode = root.addNode(newName)

            o = newNode.addPort("", PortType.OUTPUT, PortSide.EAST)

            oEdges = treeRoot.east[0].outgoingEdges
            for outputedge in list(oEdges):
                dsts = list(outputedge.dsts)
                assert len(dsts) > 0
                outputedge.remove()
                root.addHyperEdge([o, ], dsts, originObj=outputedge.originObj)

            for i, (iN, iP, iE) in enumerate(inputEdges):
                name = None
                index = len(inputEdges) - i - 1
                if hasattr(iE.originObj, "_dtype"):
                    w = iE.originObj._dtype.bit_length()
                    if w > 1:
                        name = "[%d:%d]" % ((index + 1) * w, index * w)
                    else:
                        name = None

                if name is None:
                    name = "[%d]" % (index)

                inp = newNode.addPort(name,
                                      PortType.INPUT, PortSide.WEST)
                iE.removeTarget(iP)
                iE.addTarget(inp)

            for n in reducedNodes:
                root.children.remove(n)
                reducibleChildren.remove(n)
        else:
            reducibleChildren.remove(reducedNodes[0])
def flattenTrees ( root , nodeSelector : Callable [ [ LNode ] , bool ] ) : for ch in root . children : if ch . children : flattenTrees ( ch , nodeSelector ) reducibleChildren = set ( ) for ch in root . children : if nodeSelector ( ch ) : reducibleChildren . add ( ch ) while reducibleChildren : _treeRoot = reducibleChildren . pop ( ) reducibleChildren . add ( _treeRoot ) treeRoot = searchRootOfTree ( reducibleChildren , _treeRoot ) reducedNodes , inputEdges = collectNodesInTree ( treeRoot , reducibleChildren ) if len ( reducedNodes ) > 1 : newName = reducedNodes [ 0 ] . name newNode = root . addNode ( newName ) o = newNode . addPort ( "" , PortType . OUTPUT , PortSide . EAST ) oEdges = treeRoot . east [ 0 ] . outgoingEdges for outputedge in list ( oEdges ) : dsts = list ( outputedge . dsts ) assert len ( dsts ) > 0 outputedge . remove ( ) root . addHyperEdge ( [ o , ] , dsts , originObj = outputedge . originObj ) for i , ( iN , iP , iE ) in enumerate ( inputEdges ) : name = None index = len ( inputEdges ) - i - 1 if hasattr ( iE . originObj , "_dtype" ) : w = iE . originObj . _dtype . bit_length ( ) if w > 1 : name = "[%d:%d]" % ( ( index + 1 ) * w , index * w ) else : name = None if name is None : name = "[%d]" % ( index ) inp = newNode . addPort ( name , PortType . INPUT , PortSide . WEST ) iE . removeTarget ( iP ) iE . addTarget ( inp ) for n in reducedNodes : root . children . remove ( n ) reducibleChildren . remove ( n ) else : reducibleChildren . remove ( reducedNodes [ 0 ] )
Walk all nodes and discover trees of nodes (usually operators)
    and reduce them to single node with multiple outputs

    :attention: selected nodes has to have single output
                and has to be connected to nets with single driver