I am getting the following error, please debug the error and modify the code,
RuntimeError: mat1 and mat2 shapes cannot be multiplied (8x13 and 24x64)
I also need you to verify the 'flattened_size = 20 * 24' present in the critic network.

Description of my process:
In my graph network(undirected bipartite), i have fixed topology of component nodes(11 nodes) on one side and net node(9 nodes) on another side(derived from the analog circuit netlist). where i have total 20 nodes and each nodes are having 24 node features and the 38 edges were connected between these 11 component nodes and 9 net nodes (this is my state representation the data from the environment for optimization process) and among the node features of each node where the 24th node represents its stability state. my environment need the action space of values for 13 variables from the actor network as input to the simulator circuit netlist. here few nodes [0, 3, 5, 7, 10, 13, 15, 16] are need to be check for its stability and during optimization process these nodes are need to be keep in stable state.

from my description please verify the 'in_channels', 'out_channels', 'state_dim', 'action_space'

# Initialize Hyperparameters
in_channels = 24  # Number of input features
out_channels = 13  # Number of output actions or features
state_dim = 128  # Dimension of state representation, to match critic input
action_space = 13  # Environment’s action space

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.2) # dropout=0.6
        self.gat2 = GATConv(64, out_channels, heads=1, concat=False, dropout=0.2)  # 8*8 is 64 # dropout=0.6

    def forward(self, x, edge_index):
        x = F.dropout(x, p=0.2, training=self.training) # p=0.6
        x = F.elu(self.gat1(x, edge_index))
        # After dropout and activation
        #assert torch.equal(X1[:, 0], x[:, 0]), "Order mismatch after dropout/activation."
        x = F.dropout(x, p=0.2, training=self.training) # p=0.6
        x = self.gat2(x, edge_index)

        return x


class Actor(torch.nn.Module):
    def __init__(self, gnn_model):
        super(Actor, self).__init__()
        self.gnn = gnn_model
        self.stability_head = nn.Sequential(
            nn.Linear(24, 64),  # Assuming 24 features from GNN’s processed features
            nn.ReLU(),
            nn.Linear(64, 1)    # Outputs a single stability value per node
        )
        
        # Define bounds for action space scaling
        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.181e-6, 0.181e-6, 0.181e-6, 0.181e-6, 0.181e-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 from the GNN
        processed_features = self.gnn(node_features_tensor, edge_index)
        # Example assertion (add after GNN processing)
        #assert torch.all(node_features_tensor[:, 0] == processed_features[:, 0]), "Node order mismatch!"
        
        # Initialize stability predictions - assume all nodes as stable
        stability_pred = torch.ones(processed_features.size(0), device=processed_features.device)
        
        # Define the indices of nodes that actually require stability prediction
        # Given [0, 3, 5, 7, 10, 13, 15, 16] represent tunable/unstable nodes
        unstable_nodes_idx = torch.tensor([0, 3, 5, 7, 10, 13, 15, 16], dtype=torch.long, device=processed_features.device)
        
        # Fetch features for nodes requiring stability computation
        features_for_stability = processed_features[unstable_nodes_idx]
        
        # Compute stability predictions for these nodes
        stability_pred_unstable = self.stability_head(features_for_stability).squeeze(-1)
        
        # Update the overall stability predictions
        stability_pred[unstable_nodes_idx] = stability_pred_unstable
        
       
        # Normalize and scale the GNN outputs to the action bounds for usable actions
        normalized_gnn_output = torch.tanh(processed_features)
        action_means = self.bounds_low + (self.bounds_high - self.bounds_low) * ((normalized_gnn_output + 1) / 2)
        
        # Define action variances (assuming a simple fixed form here for demonstration)
        action_log_std = torch.zeros_like(action_means)
        action_std = torch.exp(action_log_std)

        return action_means, action_std, stability_pred

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)