One of the differentiated tensor appear not to have been used in the grapgh

ctetsass: <p>I am developing a graph neural network to predict the energy and the  forces as the gradient of  the energies  when I run my code I have this error  "One of the differentiated tensors appears not to have been used in the graph "</p>
<pre><code class="lang-auto">class ForceModule(MessagePassing):
    def __init__(self, num_node_features, num_edge_features, hidden_channels, num_targets=1, device='cpu'):
        super(ForceModule, self).__init__(aggr='add')
        self.num_node_features = num_node_features
        self.num_edge_features = num_edge_features
        self.hidden_channels = hidden_channels
        self.num_targets=num_targets
        self.device = device

        # Define the parameters for the message passing function

        # Define the node and edge embeddings
        self.node_embedding = nn.Linear(self.num_node_features, self.hidden_channels)
        self.edge_embedding = nn.Linear(self.num_edge_features, self.hidden_channels)
        # Define the final output layer
        self.output_layer = nn.Linear(self.hidden_channels, self.num_targets)


   
    def _forward(self, data):

        x = self.node_embedding(data.x)
        edge_attr = self.edge_embedding(data.edge_attr) # messages

        # Incorporate the angles into the node representations
        edge_angle = torch.unsqueeze(data.angle, 1)
        edge_angle = edge_angle.repeat(1, self.hidden_channels)

        # Concatenate the angle with the edge features
        edge_attr = torch.cat([edge_attr, edge_angle], dim=1)

        # Move the tensors to the specified device
        #x, edge_attr = x.to(self.device), edge_attr.to(self.device)


        # Pass messages between nodes
        # hidden = self.propagate(x=x, edge_index=edge_index, edge_attr=edge_attr, norm=data.distances, size=size)

        hidden = self.propagate(x=x, edge_index=data.edge_index, edge_attr=edge_attr)

        hidden = F.relu(hidden)

        # Perform graph readout operation.
        x = global_mean_pool(hidden, data.batch) # perform global mean pooling
        #x = global_max_pool(hidden, batch) # perform global max pooling

        # Apply the output layer
        x = self.output_layer(x)
        return x

    def forward(self, data):

        # Move the tensors to the specified device

        energy = self._forward(data)

        data.pos.requires_grad_(True)

        # Compute gradients using the copy of data.pos

        forces = -1 * torch.autograd.grad(energy, data.pos, grad_outputs=torch.ones_like(energy), create_graph=True, retain_graph=True)[0]



        return energy.view(-1), forces
</code></pre>
<p>“DataBatch(pos=[216, 3], cell=[2, 3, 3], atomic_numbers=[216], natoms=[2], tags=[216], edge_index=[2, 2506], cell_offsets=[2506, 3], y=[2], force=[216, 3], distances=[2506], fixed=[216], sid=[2], fid=[2], id=[2], x=[216, 92], edge_attr=[2506, 3], angle=[2506], batch=[216], ptr=[3])”</p>
<pre><code class="lang-auto">force_module = ForceModule(num_node_features=92, num_edge_features=3, hidden_channels=256, num_targets=1)
energy, forces = force_module(databatch)
</code></pre>
<p>Runtimme error: <strong>One of the differentiated Tensors appears to not have been used in the graph</strong></p>

ptrblck: <p>I don’t know how <code>data.pos</code> relates to the other attributes of <code>data</code> but it seems you are indeed never using <code>data.pos</code> in the <code>forward</code> method, which explains this error.<br>
Could you explain how and where <code>data.pos</code> is used to create <code>energy</code>?</p>

ctetsass: <p>Thank you for your response . I appreciate your help in identifying the issue with the “data.pos” attribute in the code. I actually used the “data.pos” attribute to calculate the “edges” attribute.<br>
However, I realized that I did this calculation before the “forward” method, which caused the error you mentioned. Therefore, I will move the calculation of “edges” to the “forward” method and give it a try as you suggested  give you a feedback</p>

ctetsass: <p>Hello, I have updated my code, and I an calculating the edge_attr using data.pos  in the forward method  but I still get the same error message</p>
<pre><code class="lang-auto">class ForceModule(MessagePassing):
    def __init__(self, num_node_features,
                 num_edge_features,
                 hidden_channels,
                 num_targets=1,
                 cutoff=None,
                 max_neighbors=None,
                 use_pbc=None,
                 otf_graph=None,
                 embeddings='khot',
                 device='cpu'
                 ):
        super(ForceModule, self).__init__(aggr='add')
        self.num_node_features = num_node_features
        self.num_edge_features = num_edge_features
        self.hidden_channels = hidden_channels
        self.num_targets=num_targets
        self.device = device
        self.cutoff= cutoff
        self.max_neighbors = max_neighbors
        self.use_pbc = use_pbc
        self.otf_graph = otf_graph
        self.embeddings=embeddings

        # Define the parameters for the message passing function

        # Define the node and edge embeddings
        self.node_embedding = nn.Linear(self.num_node_features, self.hidden_channels)
        self.edge_embedding = nn.Linear(self.num_edge_features, self.hidden_channels)
        # Define the final output layer
        self.output_layer = nn.Linear(self.hidden_channels, self.num_targets)

    def compute_graph_data(self,data,
                       cutoff=None,
                       max_neighbors=None,
                       use_pbc=None,
                       otf_graph=None):
        cutoff = cutoff or self.cutoff
        max_neighbors = max_neighbors or self.max_neighbors
        use_pbc = use_pbc or self.use_pbc
        otf_graph = otf_graph or self.otf_graph
        #embeddings = embeddings or self.embeddings

        # Get atom embeddings
        if self.embeddings == "khot":
            embeddings = KHOT_EMBEDDINGS
        elif self.embeddings == "qmof":\
            embeddings = QMOF_KHOT_EMBEDDINGS
        else:
            raise ValueError(
                    'embedding mnust be either "khot" for original CGCNN K-hot elemental embeddings or "qmof" for QMOF K-hot elemental embeddings'
                )
        embedding = torch.zeros(100, len(embeddings[1]))
        for i in range(100):
            embedding[i] = torch.tensor(embeddings[i + 1])


        node_attr = embedding[data.atomic_numbers.long() - 1]

        if not otf_graph:
            try:
                edge_index = data.edge_index
                if use_pbc:
                    cell_offsets = data.cell_offsets
                    #neighbors = data.neighbors
                    neighbors = compute_neighbors(data, edge_index)
                    data.neighbors = neighbors

            except AttributeError:
                logger.warning(
                    "Turning otf_graph=True as required attributes not present in data object"
                )
                otf_graph = True

        if use_pbc:
            if otf_graph:
                edge_index, cell_offsets, neighbors = radius_graph_pbc(
                    data, cutoff, max_neighbors
                )

            out = get_pbc_distances(
                data.pos,
                edge_index,
                data.cell,
                cell_offsets,
                neighbors,
                return_offsets=True,
                return_distance_vec=True,
            )

            edge_index = out["edge_index"]
            edge_dist = out["distances"]
            cell_offset_distances = out["offsets"]
            distance_vec = out["distance_vec"]
        else:
            if otf_graph:
                edge_index = radius_graph(
                    data.pos,
                    r=cutoff,
                    batch=data.batch,
                    max_num_neighbors=max_neighbors,
                )
            print(edge_index.shape)
            j, i = edge_index
            distance_vec = data.pos[j] - data.pos[i]

            edge_dist = distance_vec.norm(dim=-1)
            cell_offsets = torch.zeros(
                edge_index.shape[1], 3, device=data.pos.device
            )
            cell_offset_distances = torch.zeros_like(
                cell_offsets, device=data.pos.device
            )


        edge_vecs = data.pos[data.edge_index[1]] - data.pos[data.edge_index[0]]




        # Calculate the angles between adjacent edge orientations
        angles = torch.zeros(len(data.edge_index[0]))
        unique_edges, counts = torch.unique(data.edge_index[0], return_counts=True)
        for i, edge_idx in enumerate(unique_edges):
            edge_mask = (data.edge_index[0] == edge_idx)
            edge_vecs_i = edge_vecs[edge_mask]
            angles_i = torch.acos(torch.einsum('ij, ij -&gt; i', edge_vecs_i[:-1], edge_vecs_i[1:]))
            angles_i = torch.cat([angles_i, torch.tensor([0.0])])
            angles[edge_mask] = angles_i

        #edge_vecs_normed = torch.matmul( edge_vecs, torch.inverse(data.cell[0]))

        edge_attr = edge_vecs

        return (
            node_attr,
            edge_index,
            edge_attr,
            edge_vecs,
            angles
        )

    def _forward(self, data):

        (data.x,
            data.edge_index,
            data.edge_attr,
            data.edge_vecs,
            data.edge_angle)=self.compute_graph_data(data)



        # Apply the node and edge embeddings
        x = self.node_embedding(data.x)
        edge_attr = self.edge_embedding(data.edge_attr) # messages


        edge_angle = torch.unsqueeze(data.edge_angle, 1)
        edge_angle = edge_angle.repeat(1, self.hidden_channels)

        # Concatenate the angle with the edge features
        edge_attr = torch.cat([edge_attr, edge_angle], dim=1)


        hidden = self.propagate(x=x, edge_index=data.edge_index, edge_attr=edge_attr)

        hidden = F.relu(hidden)

        # Perform graph readout operation.
        x = global_mean_pool(hidden, data.batch) # perform global mean pooling
        #x = global_max_pool(hidden, batch) # perform global max pooling

        # Apply the output layer
        x = self.output_layer(x)
        return x


    def __repr__(self) -&gt; str:
        return (f'{self.__class__.__name__}({self.num_edge_features}, '
                f'{self.num_edge_features}, {self.hidden_channels}'
                f'num_targets={self.num_targets})')


    def forward(self, data):

        # Move the tensors to the specified device

        energy = self._forward(data)

        data.pos.requires_grad_(True)

        # Compute gradients using the copy of data.pos

        forces = -1 * torch.autograd.grad(energy, data.pos, grad_outputs=torch.ones_like(energy), create_graph=True, retain_graph=True)[0]



        return energy.view(-1), forces


</code></pre>

ptrblck: <p>Calling <code>data.pos.requires_grad_(True)</code> looks wrong as this tensor should already require gradients during the forward pass if you want to calculate them afterwards via <code>torch.autograd.grad</code>.<br>
What happens if you remove this line of code or set it before the forward pass?</p>

ctetsass: <p>Please find the updated code below. I have moved the line “databatch.pos.requires_grad_(True)” before the forward method call. It’s possible that one of the transformations I made in the _forward method has disrupted the dependency chain between pos → edge_attr → energy?</p>
<pre><code class="lang-auto">class ForceModule(MessagePassing):
    def __init__(self, num_node_features,
                 num_edge_features,
                 hidden_channels,
                 num_targets=1,
                 cutoff=None,
                 max_neighbors=None,
                 use_pbc=None,
                 otf_graph=None,
                 embeddings='khot',
                 device='cpu'
                 ):
        super(ForceModule, self).__init__(aggr='add')
        self.num_node_features = num_node_features
        self.num_edge_features = num_edge_features
        self.hidden_channels = hidden_channels
        self.num_targets=num_targets
        self.device = device
        self.cutoff= cutoff
        self.max_neighbors = max_neighbors
        self.use_pbc = use_pbc
        self.otf_graph = otf_graph
        self.embeddings=embeddings

        # Define the parameters for the message passing function

        # Define the node and edge embeddings
        self.node_embedding = nn.Linear(self.num_node_features, self.hidden_channels)
        self.edge_embedding = nn.Linear(self.num_edge_features, self.hidden_channels)
        # Define the final output layer
        self.output_layer = nn.Linear(self.hidden_channels, self.num_targets)

    def compute_graph_data(self,data,
                       cutoff=None,
                       max_neighbors=None,
                       use_pbc=None,
                       otf_graph=None):
        cutoff = cutoff or self.cutoff
        max_neighbors = max_neighbors or self.max_neighbors
        use_pbc = use_pbc or self.use_pbc
        otf_graph = otf_graph or self.otf_graph
        #embeddings = embeddings or self.embeddings

        # Get atom embeddings
        if self.embeddings == "khot":
            embeddings = KHOT_EMBEDDINGS
        elif self.embeddings == "qmof":\
            embeddings = QMOF_KHOT_EMBEDDINGS
        else:
            raise ValueError(
                    'embedding mnust be either "khot" for original CGCNN K-hot elemental embeddings or "qmof" for QMOF K-hot elemental embeddings'
                )
        embedding = torch.zeros(100, len(embeddings[1]))
        for i in range(100):
            embedding[i] = torch.tensor(embeddings[i + 1])


        node_attr = embedding[data.atomic_numbers.long() - 1]

        if not otf_graph:
            try:
                edge_index = data.edge_index
                if use_pbc:
                    cell_offsets = data.cell_offsets
                    #neighbors = data.neighbors
                    neighbors = compute_neighbors(data, edge_index)
                    data.neighbors = neighbors

            except AttributeError:
                logger.warning(
                    "Turning otf_graph=True as required attributes not present in data object"
                )
                otf_graph = True

        if use_pbc:
            if otf_graph:
                edge_index, cell_offsets, neighbors = radius_graph_pbc(
                    data, cutoff, max_neighbors
                )

            out = get_pbc_distances(
                data.pos,
                edge_index,
                data.cell,
                cell_offsets,
                neighbors,
                return_offsets=True,
                return_distance_vec=True,
            )

            edge_index = out["edge_index"]
            edge_dist = out["distances"]
            cell_offset_distances = out["offsets"]
            distance_vec = out["distance_vec"]
        else:
            if otf_graph:
                edge_index = radius_graph(
                    data.pos,
                    r=cutoff,
                    batch=data.batch,
                    max_num_neighbors=max_neighbors,
                )
            print(edge_index.shape)
            j, i = edge_index
            distance_vec = data.pos[j] - data.pos[i]

            edge_dist = distance_vec.norm(dim=-1)
            cell_offsets = torch.zeros(
                edge_index.shape[1], 3, device=data.pos.device
            )
            cell_offset_distances = torch.zeros_like(
                cell_offsets, device=data.pos.device
            )


        edge_vecs = data.pos[data.edge_index[1]] - data.pos[data.edge_index[0]]




        # Calculate the angles between adjacent edge orientations
        angles = torch.zeros(len(data.edge_index[0]))
        unique_edges, counts = torch.unique(data.edge_index[0], return_counts=True)
        for i, edge_idx in enumerate(unique_edges):
            edge_mask = (data.edge_index[0] == edge_idx)
            edge_vecs_i = edge_vecs[edge_mask]
            angles_i = torch.acos(torch.einsum('ij, ij -&gt; i', edge_vecs_i[:-1], edge_vecs_i[1:]))
            angles_i = torch.cat([angles_i, torch.tensor([0.0])])
            angles[edge_mask] = angles_i

        #edge_vecs_normed = torch.matmul( edge_vecs, torch.inverse(data.cell[0]))

        edge_attr = edge_vecs

        return (
            node_attr,
            edge_index,
            edge_attr,
            edge_vecs,
            angles
        )

    def _forward(self, data):

        (data.x,
            data.edge_index,
            data.edge_attr,
            data.edge_vecs,
            data.edge_angle)=self.compute_graph_data(data)



        # Apply the node and edge embeddings
        x = self.node_embedding(data.x)
        edge_attr = self.edge_embedding(data.edge_attr) # messages


        edge_angle = torch.unsqueeze(data.edge_angle, 1)
        edge_angle = edge_angle.repeat(1, self.hidden_channels)

        # Concatenate the angle with the edge features
        edge_attr = torch.cat([edge_attr, edge_angle], dim=1)


        hidden = self.propagate(x=x, edge_index=data.edge_index, edge_attr=edge_attr)

        hidden = F.relu(hidden)

        # Perform graph readout operation.
        x = global_mean_pool(hidden, data.batch) # perform global mean pooling
        #x = global_max_pool(hidden, batch) # perform global max pooling

        # Apply the output layer
        x = self.output_layer(x)
        print(data.x)

        return x


    def __repr__(self) -&gt; str:
        return (f'{self.__class__.__name__}({self.num_edge_features}, '
                f'{self.num_edge_features}, {self.hidden_channels}'
                f'num_targets={self.num_targets})')


    def forward(self, data):

        # Move the tensors to the specified device

        energy = self._forward(data)

        # Compute gradients using the copy of data.pos

        forces = -1 * torch.autograd.grad(energy, data.pos, grad_outputs=torch.ones_like(energy), create_graph=True, retain_graph=True)[0]



        return energy.view(-1), forces


</code></pre>
<pre><code class="lang-auto">force_module = ForceModule(num_node_features=92,
                           num_edge_features=3,
                           hidden_channels=256,
                           num_targets=1,
                           cutoff=6,
                           max_neighbors=50,
                           use_pbc=True,
                           otf_graph=False,
                           embeddings='khot',
                           device='cpu' )

databatch.pos.requires_grad_(True)
energy, forces = force_module(databatch)

print(energy)
print(forces)
</code></pre>

ctetsass: <p>I resolved it by adding a message method to my class. Thank you for you<br>
help</p>
<pre><code class="lang-auto">def message(self, x_i, x_j, edge_attr):
   
        return x_i + x_j + edge_attr
</code></pre>