i need you to remember my entire code which is needed for further updation i am having very lengthy code so, i will provide my entire code into three splitups in sequence.

Splitup1:
class CustomGNN(torch.nn.Module):
    def __init__(self, in_channels, out_channels):
        super(CustomGNN, self).__init__()
        self.gat1 = GATConv(in_channels, 8, heads=8, dropout=0.6)
        self.gat2 = GATConv(64, out_channels, heads=1, concat=False, dropout=0.6)  # 8*8 is 64

    def forward(self, x, edge_index):
        x = F.dropout(x, p=0.6, training=self.training)
        x = F.elu(self.gat1(x, edge_index))
        x = F.dropout(x, p=0.6, training=self.training)
        x = self.gat2(x, edge_index)

        # Selective feature synchronization based on given mappings
        # Converting given (row, column) pairs to 0-based indexing
        sync_mappings = {
            (10, 18): (7, 18),
            (10, 19): (7, 19),
            (16, 18): (15, 18),
            (16, 19): (15, 19),
            (5, 18): (13, 18),
            (5, 19): (13, 19),
            # No need to map (3, 18) and (3, 19) as they remain unchanged
        }
        
        original_features = x.clone()
        for target, source in sync_mappings.items():
            original_features[target[0], target[1]] = original_features[source[0], source[1]]
        
        # Only retain the transformative updates for the specified 19 features
        dynamic_updates_mask = torch.zeros_like(x, dtype=bool)
        dynamic_indices = [
            (0, 18), (0, 19), (3, 18), (3, 19), (5, 18), (5, 19), (7, 18), (7, 19),
            (10, 18), (10, 19), (13, 18), (13, 19), (15, 18), (15, 19), (16, 18),
            (16, 19), (17, 20), (18, 21), (19, 22)]
        for row, col in dynamic_indices:
            dynamic_updates_mask[row, col] = True
        
        x = original_features * (~dynamic_updates_mask) + x * dynamic_updates_mask
        return x

class Actor(torch.nn.Module):
    def __init__(self, gnn_model):
        super(Actor, self).__init__()
        self.gnn = gnn_model
        # Bounds are converted to tensors for ease of calculation
        self.bounds_low = torch.tensor([0.18e-6, 0.18e-6, 0.18e-6, 0.18e-6, 0.18e-6, 
                                        0.5e-6, 0.5e-6, 0.5e-6, 0.5e-6, 0.5e-6, 
                                        15e-6, 0.1e-12, 0.8], dtype=torch.float32)
                                        
        self.bounds_high = torch.tensor([0.2e-6, 0.2e-6, 0.2e-6, 0.2e-6, 0.2e-6, 
                                         50e-6, 50e-6, 50e-6, 50e-6, 50e-6, 
                                         30e-6, 10e-12, 1.4], dtype=torch.float32)

    def forward(self, state):
        node_features_tensor, _, edge_index = state
        processed_features = self.gnn(node_features_tensor, edge_index)
        
        # Specific (row, column) indices for action values, converted to 0-based indexing
        action_indices = [
            (10, 19), (16, 19), (5, 19), (3, 19), (0, 19), (10, 18),
            (16, 18), (5, 18), (3, 18), (0, 18), (17, 20), (18, 21), (19, 22)
        ]
        
        # For gathering specific indices, we first convert action_indices to a tensor format that can be used with gather or indexing
        action_indices_tensor = torch.tensor(action_indices, dtype=torch.long).t()
        selected_features = processed_features[action_indices_tensor[0], action_indices_tensor[1]]
     
        # Using tanh to squash gnn_output to [-1, 1]
        normalized_gnn_output = torch.tanh(selected_features)
        
        # Now scale and shift the normalized output to the action bounds
        action_means = self.bounds_low + (self.bounds_high - self.bounds_low) * ((normalized_gnn_output + 1) / 2)

        # Here we should define action variances as well, which are learnable and specific to your model structure
        action_log_std = torch.zeros_like(action_means)
        action_std = torch.exp(action_log_std)

        return action_means, action_std

class Critic(torch.nn.Module):
    def __init__(self):
        super(Critic, self).__init__()
        flattened_size = 20 * 24  # Number of nodes times features per node
        self.network = torch.nn.Sequential(
            torch.nn.Linear(flattened_size, 128),
            torch.nn.ReLU(),
            torch.nn.Linear(128, 1)
        )

    def forward(self, state):
        # Directly use state as it is already what we referred to as state[0] in evaluate
        flattened = state.view(-1)  # Flatten to [480]
        return self.network(flattened)

Next i will give you my code splitup2, which is in continuation to the code splitup1.