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

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 "

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

“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])”

force_module = ForceModule(num_node_features=92, num_edge_features=3, hidden_channels=256, num_targets=1)
energy, forces = force_module(databatch)

Runtimme error: One of the differentiated Tensors appears to not have been used in the graph

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

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.
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

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

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 -> 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) -> 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


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

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?

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 -> 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) -> 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


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)

I resolved it by adding a message method to my class. Thank you for you
help

def message(self, x_i, x_j, edge_attr):
   
        return x_i + x_j + edge_attr