compositional_graph_models/models.py
def _prepare_graph(self, graph: dgl.DGLGraph):
        """
        Transform the input graph inplace into a graph that can be used for computation.

        To process all nodes simultaneously, we add a "padding" node that acts as a
        placeholder child or parent for every node missing one of those, so that every
        node has the same in-degree (4). The padding node always has a feature vector
        of zero.

        Edges are always indexed in the order (left child, right child, parent, self).

        The following invariants hold for all nodes:
            - Leaf nodes have 2 child edges from the padding node.
            - Unary function nodes have a left edge from their child and a right edge
              from the padding node.
            - Expression roots (nodes directly under Equality nodes) have a parent
              edge from the padding node, and there are no Equality nodes.

        Parameters
        ----------
        graph
            The graph to modify. Modified in place; if this is not desirable,
            clone the graph ahead of time and pass in the clone to modify in place.
        """
        # Remove Equality nodes, splitting examples into left and right expressions
        root_mask = (
            graph.ndata[data.FUNCTION_FIELD]
            == self.hparams.function_vocab[data.EQUALITY_FUNCTION]
        )
        root_indices = torch.nonzero(root_mask, as_tuple=False)[:, 0]
        graph.remove_nodes(root_indices)

        # Save the parent -> child edges for later
        parent_graph = graph.reverse()

        # Add a "padding node" to signal missing values
        graph.add_nodes(1)
        pad_node_id = graph.num_nodes() - 1
        graph.ndata["function"][pad_node_id] = self.hparams.function_vocab[
            data.PAD_FUNCTION
        ]
        graph.ndata["token"][pad_node_id] = self.hparams.token_vocab[data.PAD_TOKEN]

        # Add edges from the padding node to all nodes with less than 2 children
        pad_count = 2 - graph.in_degrees()  # Number of times to pad each node
        pad_count[pad_node_id] = 0  # Never add input edges to the pad node

        while not (pad_count == 0).all():
            pad_mask = pad_count > 0
            pad_idx = torch.nonzero(pad_mask, as_tuple=False)[:, 0]
            graph.add_edges(pad_node_id, pad_idx)
            pad_count -= pad_mask.to(pad_count.dtype)

        # Add the parent -> child edges back in
        graph.add_edges(*parent_graph.edges())

        # Add edges from the pad node to every node without a parent, except itself
        pad_idx = torch.nonzero(graph.in_degrees() < 3, as_tuple=False)[:-1, 0]
        graph.add_edges(pad_node_id, pad_idx)

        # Add self-loops to all nodes except the pad node
        internal_indices = graph.nodes()[:-1]
        graph.add_edges(internal_indices, internal_indices)

        Transform the input graph inplace into a graph that can be used for computation.

        To process all nodes simultaneously, we add a "padding" node that acts as a
        placeholder child or parent for every node missing one of those, so that every
        node has the same in-degree (4). The padding node always has a feature vector
        of zero.

        Edges are always indexed in the order (left child, right child, parent, self).

        The following invariants hold for all nodes:
            - Leaf nodes have 2 child edges from the padding node.
            - Unary function nodes have a left edge from their child and a right edge
              from the padding node.
            - Expression roots (nodes directly under Equality nodes) have a parent
              edge from the padding node, and there are no Equality nodes.

        Parameters
        ----------
        graph
            The graph to modify. Modified in place; if this is not desirable,
            clone the graph ahead of time and pass in the clone to modify in place.
        
Transform the input graph inplace into a graph that can be used for computation.
To process all nodes simultaneously, we add a "padding" node that acts as a
placeholder child or parent for every node missing one of those, so that every
node has the same in-degree (4). The padding node always has a feature vector
of zero.

Edges are always indexed in the order (left child, right child, parent, self).

The following invariants hold for all nodes:
Leaf nodes have 2 child edges from the padding node.
Unary function nodes have a left edge from their child and a right edge
from the padding node.
Expression roots (nodes directly under Equality nodes) have a parent
edge from the padding node, and there are no Equality nodes.

Parameters

graph
The graph to modify. Modified in place; if this is not desirable,
clone the graph ahead of time and pass in the clone to modify in place.
Transform the input graph inplace into a graph that can be used for computation . To process all nodes simultaneously we add a " padding " node that acts as a placeholder child or parent for every node missing one of those so that every node has the same in - degree ( 4 ) . The padding node always has a feature vector of zero . Edges are always indexed in the order ( left child right child parent self ) . The following invariants hold for all nodes : Leaf nodes have 2 child edges from the padding node . Unary function nodes have a left edge from their child and a right edge from the padding node . Expression roots ( nodes directly under Equality nodes ) have a parent edge from the padding node and there are no Equality nodes . Parameters graph The graph to modify . Modified in place ; if this is not desirable clone the graph ahead of time and pass in the clone to modify in place .
def _prepare_graph(self, graph: dgl.DGLGraph):
        root_mask = (
            graph.ndata[data.FUNCTION_FIELD]
            == self.hparams.function_vocab[data.EQUALITY_FUNCTION]
        )
        root_indices = torch.nonzero(root_mask, as_tuple=False)[:, 0]
        graph.remove_nodes(root_indices)
        parent_graph = graph.reverse()
        graph.add_nodes(1)
        pad_node_id = graph.num_nodes() - 1
        graph.ndata["function"][pad_node_id] = self.hparams.function_vocab[
            data.PAD_FUNCTION
        ]
        graph.ndata["token"][pad_node_id] = self.hparams.token_vocab[data.PAD_TOKEN]
        pad_count = 2 - graph.in_degrees()  
        pad_count[pad_node_id] = 0  
        while not (pad_count == 0).all():
            pad_mask = pad_count > 0
            pad_idx = torch.nonzero(pad_mask, as_tuple=False)[:, 0]
            graph.add_edges(pad_node_id, pad_idx)
            pad_count -= pad_mask.to(pad_count.dtype)
        graph.add_edges(*parent_graph.edges())
        pad_idx = torch.nonzero(graph.in_degrees() < 3, as_tuple=False)[:-1, 0]
        graph.add_edges(pad_node_id, pad_idx)
        internal_indices = graph.nodes()[:-1]
        graph.add_edges(internal_indices, internal_indices)
def _prepare_graph ( self , graph : dgl . DGLGraph ) : root_mask = ( graph . ndata [ data . FUNCTION_FIELD ] == self . hparams . function_vocab [ data . EQUALITY_FUNCTION ] ) root_indices = torch . nonzero ( root_mask , as_tuple = False ) [ : , 0 ] graph . remove_nodes ( root_indices ) parent_graph = graph . reverse ( ) graph . add_nodes ( 1 ) pad_node_id = graph . num_nodes ( ) - 1 graph . ndata [ "function" ] [ pad_node_id ] = self . hparams . function_vocab [ data . PAD_FUNCTION ] graph . ndata [ "token" ] [ pad_node_id ] = self . hparams . token_vocab [ data . PAD_TOKEN ] pad_count = 2 - graph . in_degrees ( ) pad_count [ pad_node_id ] = 0 while not ( pad_count == 0 ) . all ( ) : pad_mask = pad_count > 0 pad_idx = torch . nonzero ( pad_mask , as_tuple = False ) [ : , 0 ] graph . add_edges ( pad_node_id , pad_idx ) pad_count -= pad_mask . to ( pad_count . dtype ) graph . add_edges ( * parent_graph . edges ( ) ) pad_idx = torch . nonzero ( graph . in_degrees ( ) < 3 , as_tuple = False ) [ : - 1 , 0 ] graph . add_edges ( pad_node_id , pad_idx ) internal_indices = graph . nodes ( ) [ : - 1 ] graph . add_edges ( internal_indices , internal_indices )
Transform the input graph inplace into a graph that can be used for computation.
Transform the input graph inplace into a graph that can be used for computation .
"""
        Transform the input graph inplace into a graph that can be used for computation.

        To process all nodes simultaneously, we add a "padding" node that acts as a
        placeholder child or parent for every node missing one of those, so that every
        node has the same in-degree (4). The padding node always has a feature vector
        of zero.

        Edges are always indexed in the order (left child, right child, parent, self).

        The following invariants hold for all nodes:
            - Leaf nodes have 2 child edges from the padding node.
            - Unary function nodes have a left edge from their child and a right edge
              from the padding node.
            - Expression roots (nodes directly under Equality nodes) have a parent
              edge from the padding node, and there are no Equality nodes.

        Parameters
        ----------
        graph
            The graph to modify. Modified in place; if this is not desirable,
            clone the graph ahead of time and pass in the clone to modify in place.
        """ # Remove Equality nodes, splitting examples into left and right expressions # Save the parent -> child edges for later # Add a "padding node" to signal missing values # Add edges from the padding node to all nodes with less than 2 children # Number of times to pad each node # Never add input edges to the pad node # Add the parent -> child edges back in # Add edges from the pad node to every node without a parent, except itself # Add self-loops to all nodes except the pad node
self
graph
dgl.DGLGraph