| |
| import torch |
| import torch.nn as nn |
|
|
| |
| class LossWeights: |
| lambda_task: float = 1.0 |
| lambda_res: float = 0.5 |
| lambda_ent: float = 0.2 |
|
|
|
|
| |
| class RRF_Ultra_CNN(nn.Module): |
| def __init__(self, input_dim=1, output_dim=1): |
| super(RRF_Ultra_CNN, self).__init__() |
| self.conv1 = nn.Conv1d(input_dim, 64, kernel_size=3, padding=1) |
| self.conv2 = nn.Conv1d(64, 128, kernel_size=3, padding=1) |
| self.fc1 = nn.Linear(128*160, 256) |
| self.fc2 = nn.Linear(256, output_dim) |
|
|
| def forward(self, x): |
| x = F.relu(self.conv1(x)) |
| x = F.relu(self.conv2(x)) |
| x = torch.flatten(x, 1) |
| x = F.relu(self.fc1(x)) |
| return torch.sigmoid(self.fc2(x)) |
|
|
|
|
| |
| class SavantRRF_Gauge(nn.Module): |
| def __init__(self, input_dim, hidden_dim, output_dim): |
| super(SavantRRF_Gauge, self).__init__() |
| self.conv1 = nn.Conv1d(input_dim, 64, kernel_size=3, padding=1) |
| self.conv2 = nn.Conv1d(64, 128, kernel_size=3, padding=1) |
| self.conv3 = nn.Conv1d(128, 256, kernel_size=3, padding=1) |
| self.dropout = nn.Dropout(0.25) |
| |
| |
| |
| self.fc1 = nn.Linear(256*160, 512) |
| self.fc2 = nn.Linear(512, 256) |
| self.fc3 = nn.Linear(256, output_dim) |
|
|
| def forward(self, x): |
| x = F.relu(self.conv1(x)) |
| x = F.relu(self.conv2(x)) |
| x = F.relu(self.conv3(x)) |
| x = torch.flatten(x, 1) |
| x = self.dropout(x) |
| x = F.relu(self.fc1(x)) |
| x = F.relu(self.fc2(x)) |
| return torch.sigmoid(self.fc3(x)) |
|
|
|
|
| |
| class DiracGraphConv(nn.Module): |
| def __init__(self, in_dim: int, out_dim: int, alpha: float = 1.0, bias: bool = True): |
| super().__init__() |
| self.lin = nn.Linear(in_dim, out_dim, bias=bias) |
| self.alpha = nn.Parameter(torch.tensor(alpha, dtype=torch.float32)) |
| self.bias_edge = nn.Parameter(torch.tensor(0.0, dtype=torch.float32)) |
|
|
| @staticmethod |
| def cosine_corr(z_i: torch.Tensor, z_j: torch.Tensor, eps: float = 1e-9) -> torch.Tensor: |
| num = (z_i * z_j).sum(dim=-1) |
| den = torch.clamp(z_i.norm(dim=-1) * z_j.norm(dim=-1), min=eps) |
| return num / den |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| N = x.size(0) |
| row, col = edge_index |
| corr = self.cosine_corr(z[row], z[col]) |
| logits = self.alpha * corr + self.bias_edge |
| device = x.device |
| E = row.size(0) |
| ones = torch.ones(E, device=device) |
| max_per_row = torch.full((N,), -1e9, device=device) |
| max_per_row = max_per_row.index_put((row,), logits, accumulate=False).scatter_reduce_(0, row, logits, reduce="amax") |
| logits_centered = logits - max_per_row[row] |
| exp_logits = torch.exp(logits_centered) |
| denom = torch.zeros(N, device=device).index_add_(0, row, exp_logits) |
| attn = exp_logits / (denom[row] + 1e-9) |
| deg = torch.zeros(N, device=device).index_add_(0, row, ones) |
| norm = 1.0 / torch.clamp(deg[row], min=1.0) |
| msgs = norm.unsqueeze(-1) * attn.unsqueeze(-1) * x[col] |
| out = torch.zeros_like(x).index_add_(0, row, msgs) |
| return self.lin(out) |
|
|
|
|
| |
| class GNNDiracRRF(nn.Module): |
| def __init__(self, in_dim: int, hidden_dim: int, out_dim: int, num_layers: int, z_dim: int, |
| alpha_attn: float = 1.0, dropout: float = 0.1): |
| super().__init__() |
| self.z_dim = z_dim |
| self.layers = nn.ModuleList() |
| self.layers.append(DiracGraphConv(in_dim, hidden_dim, alpha=alpha_attn)) |
| for _ in range(num_layers - 2): |
| self.layers.append(DiracGraphConv(hidden_dim, hidden_dim, alpha=alpha_attn)) |
| self.layers.append(DiracGraphConv(hidden_dim, out_dim, alpha=alpha_attn)) |
| self.dropout = nn.Dropout(dropout) |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| h = x |
| for i, layer in enumerate(self.layers): |
| h = layer(h, edge_index, z) |
| if i < len(self.layers) - 1: |
| h = F.gelu(h) |
| h = self.dropout(h) |
| return h |
|
|
|
|
| |
| class LossWeights: |
| lambda_task: float = 1.0 |
| lambda_res: float = 0.5 |
| lambda_ent: float = 0.2 |
|
|
|
|
| |
| class IcosahedralRRF(nn.Module): |
| def __init__(self, input_dim, hidden_dim, output_dim, gnn_num_layers=2, gnn_z_dim=16, gnn_alpha_attn=1.0, gnn_dropout=0.1): |
| super(IcosahedralRRF, self).__init__() |
| |
| self.nodes = nn.ModuleList([ |
| SavantRRF_Gauge(input_dim, hidden_dim, output_dim) for _ in range(12) |
| ]) |
| |
| |
| |
| |
| self.ethical_core = nn.Linear(12 * output_dim, output_dim) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| self.memory_map = GNNDiracRRF(in_dim=output_dim, |
| hidden_dim=hidden_dim, |
| out_dim=output_dim, |
| num_layers=gnn_num_layers, |
| z_dim=gnn_z_dim, |
| alpha_attn=gnn_alpha_attn, |
| dropout=gnn_dropout) |
|
|
|
|
| def forward(self, x, edge_index=None, z=None): |
| |
| outputs = [node(x) for node in self.nodes] |
| |
|
|
| |
| concat = torch.cat(outputs, dim=1) |
| regulated = torch.sigmoid(self.ethical_core(concat)) |
|
|
| |
| if edge_index is not None and z is not None: |
| |
| |
| stacked_outputs = torch.stack(outputs, dim=1) |
|
|
| |
| |
| |
|
|
| gnn_outputs_list = [] |
| for i in range(stacked_outputs.size(0)): |
| |
| gnn_input_features_i = stacked_outputs[i] |
|
|
| |
| edge_index_i = edge_index.to(x.device) |
| z_i = z.to(x.device) |
|
|
| |
| gnn_output_i = self.memory_map(gnn_input_features_i, edge_index_i, z_i) |
| gnn_outputs_list.append(gnn_output_i) |
|
|
| |
| gnn_outputs_stacked = torch.stack(gnn_outputs_list, dim=0) |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| aggregated_gnn_output = gnn_outputs_stacked.mean(dim=1) |
|
|
| return aggregated_gnn_output |
|
|
| else: |
| |
| return regulated |
|
|
|
|
| |
| class RRF_Ultra_CNN(nn.Module): |
| def __init__(self, input_dim=1, output_dim=1): |
| super(RRF_Ultra_CNN, self).__init__() |
| self.conv1 = nn.Conv1d(input_dim, 64, kernel_size=3, padding=1) |
| self.conv2 = nn.Conv1d(64, 128, kernel_size=3, padding=1) |
| self.fc1 = nn.Linear(128*160, 256) |
| self.fc2 = nn.Linear(256, output_dim) |
|
|
| def forward(self, x): |
| x = F.relu(self.conv1(x)) |
| x = F.relu(self.conv2(x)) |
| x = torch.flatten(x, 1) |
| x = F.relu(self.fc1(x)) |
| return torch.sigmoid(self.fc2(x)) |
|
|
|
|
| |
| class SavantRRF_Gauge(nn.Module): |
| def __init__(self, input_dim, hidden_dim, output_dim): |
| super(SavantRRF_Gauge, self).__init__() |
| self.conv1 = nn.Conv1d(input_dim, 64, kernel_size=3, padding=1) |
| self.conv2 = nn.Conv1d(64, 128, kernel_size=3, padding=1) |
| self.conv3 = nn.Conv1d(128, 256, kernel_size=3, padding=1) |
| self.dropout = nn.Dropout(0.25) |
| |
| |
| |
| self.fc1 = nn.Linear(256*160, 512) |
| self.fc2 = nn.Linear(512, 256) |
| self.fc3 = nn.Linear(256, output_dim) |
|
|
| def forward(self, x): |
| x = F.relu(self.conv1(x)) |
| x = F.relu(self.conv2(x)) |
| x = F.relu(self.conv3(x)) |
| x = torch.flatten(x, 1) |
| x = self.dropout(x) |
| x = F.relu(self.fc1(x)) |
| x = F.relu(self.fc2(x)) |
| return torch.sigmoid(self.fc3(x)) |
|
|
|
|
| |
| class DiracGraphConv(nn.Module): |
| def __init__(self, in_dim: int, out_dim: int, alpha: float = 1.0, bias: bool = True): |
| super().__init__() |
| self.lin = nn.Linear(in_dim, out_dim, bias=bias) |
| self.alpha = nn.Parameter(torch.tensor(alpha, dtype=torch.float32)) |
| self.bias_edge = nn.Parameter(torch.tensor(0.0, dtype=torch.float32)) |
|
|
| @staticmethod |
| def cosine_corr(z_i: torch.Tensor, z_j: torch.Tensor, eps: float = 1e-9) -> torch.Tensor: |
| num = (z_i * z_j).sum(dim=-1) |
| den = torch.clamp(z_i.norm(dim=-1) * z_j.norm(dim=-1), min=eps) |
| return num / den |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| N = x.size(0) |
| row, col = edge_index |
| corr = self.cosine_corr(z[row], z[col]) |
| logits = self.alpha * corr + self.bias_edge |
| device = x.device |
| E = row.size(0) |
| ones = torch.ones(E, device=device) |
| max_per_row = torch.full((N,), -1e9, device=device) |
| max_per_row = max_per_row.index_put((row,), logits, accumulate=False).scatter_reduce_(0, row, logits, reduce="amax") |
| logits_centered = logits - max_per_row[row] |
| exp_logits = torch.exp(logits_centered) |
| denom = torch.zeros(N, device=device).index_add_(0, row, exp_logits) |
| attn = exp_logits / (denom[row] + 1e-9) |
| deg = torch.zeros(N, device=device).index_add_(0, row, ones) |
| norm = 1.0 / torch.clamp(deg[row], min=1.0) |
| msgs = norm.unsqueeze(-1) * attn.unsqueeze(-1) * x[col] |
| out = torch.zeros_like(x).index_add_(0, row, msgs) |
| return self.lin(out) |
|
|
|
|
| |
| class GNNDiracRRF(nn.Module): |
| def __init__(self, in_dim: int, hidden_dim: int, out_dim: int, num_layers: int, z_dim: int, |
| alpha_attn: float = 1.0, dropout: float = 0.1): |
| super().__init__() |
| self.z_dim = z_dim |
| self.layers = nn.ModuleList() |
| self.layers.append(DiracGraphConv(in_dim, hidden_dim, alpha=alpha_attn)) |
| for _ in range(num_layers - 2): |
| self.layers.append(DiracGraphConv(hidden_dim, hidden_dim, alpha=alpha_attn)) |
| self.layers.append(DiracGraphConv(hidden_dim, out_dim, alpha=alpha_attn)) |
| self.dropout = nn.Dropout(dropout) |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| h = x |
| for i, layer in enumerate(self.layers): |
| h = layer(h, edge_index, z) |
| if i < len(self.layers) - 1: |
| h = F.gelu(h) |
| h = self.dropout(h) |
| return h |
|
|
|
|
| |
| class LossWeights: |
| lambda_task: float = 1.0 |
| lambda_res: float = 0.5 |
| lambda_ent: float = 0.2 |
|
|
|
|
| |
| class IcosahedralRRF(nn.Module): |
| def __init__(self, input_dim, hidden_dim, output_dim, gnn_num_layers=2, gnn_z_dim=16, gnn_alpha_attn=1.0, gnn_dropout=0.1): |
| super(IcosahedralRRF, self).__init__() |
| |
| self.nodes = nn.ModuleList([ |
| SavantRRF_Gauge(input_dim, hidden_dim, output_dim) for _ in range(12) |
| ]) |
| |
| |
| |
| |
| self.ethical_core = nn.Linear(12 * output_dim, output_dim) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| self.memory_map = GNNDiracRRF(in_dim=output_dim, |
| hidden_dim=hidden_dim, |
| out_dim=output_dim, |
| num_layers=gnn_num_layers, |
| z_dim=gnn_z_dim, |
| alpha_attn=gnn_alpha_attn, |
| dropout=gnn_dropout) |
|
|
|
|
| def forward(self, x, edge_index=None, z=None): |
| |
| outputs = [node(x) for node in self.nodes] |
| |
|
|
| |
| concat = torch.cat(outputs, dim=1) |
| regulated = torch.sigmoid(self.ethical_core(concat)) |
|
|
| |
| if edge_index is not None and z is not None: |
| |
| |
| stacked_outputs = torch.stack(outputs, dim=1) |
|
|
| |
| |
| |
|
|
| gnn_outputs_list = [] |
| for i in range(stacked_outputs.size(0)): |
| |
| gnn_input_features_i = stacked_outputs[i] |
|
|
| |
| edge_index_i = edge_index.to(x.device) |
| z_i = z.to(x.device) |
|
|
| |
| gnn_output_i = self.memory_map(gnn_input_features_i, edge_index_i, z_i) |
| gnn_outputs_list.append(gnn_output_i) |
|
|
| |
| gnn_outputs_stacked = torch.stack(gnn_outputs_list, dim=0) |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| aggregated_gnn_output = gnn_outputs_stacked.mean(dim=1) |
|
|
| return aggregated_gnn_output |
|
|
| else: |
| |
| return regulated |
|
|
|
|
| |
| class LossWeights: |
| lambda_task: float = 1.0 |
| lambda_res: float = 0.5 |
| lambda_ent: float = 0.2 |
|
|
|
|
| |
| class IcosahedralRRFDataset(InMemoryDataset): |
| def __init__(self, num_graphs: int = 64, k_modes: int = 16, feat_dim: int = 8, |
| task_type: str = 'classification', split: str = 'train', transform=None, pre_transform=None): |
| super().__init__('.', transform, pre_transform) |
| self.task_type = task_type |
| self.num_graphs = num_graphs |
| self.k_modes = k_modes |
| self.feat_dim = feat_dim |
|
|
| |
| data_list = [] |
| rng = np.random.default_rng(42 if split == 'train' else (43 if split == 'val' else 44)) |
|
|
| for i in range(num_graphs): |
| G = nx.icosahedral_graph() |
| n_nodes = G.number_of_nodes() |
|
|
| |
| D = build_dirac_operator(G, normalize=True) |
| |
| vals, vecs = dirac_eigendecomp(D, k=k_modes) |
| Z = node_spectral_coords_from_dirac(vecs, n_nodes) |
|
|
| |
| edge_list = list(G.edges()) |
| edge_index = torch.tensor(edge_list, dtype=torch.long).t().contiguous() |
| |
| row, col = edge_index |
| edge_index = torch.cat([edge_index, torch.stack([col, row], dim=0)], dim=1) |
|
|
| |
| |
| x = torch.randn(n_nodes, feat_dim, dtype=torch.float32) |
|
|
| |
| if task_type == 'classification': |
| |
| threshold = 0.0 |
| y = (x.sum(dim=-1) > threshold).long() |
| elif task_type == 'regression': |
| |
| y = x.sum(dim=-1) |
| else: |
| raise ValueError("task_type must be 'classification' or 'regression'") |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| |
| |
|
|
| |
| |
|
|
| |
|
|
| |
| if 'x_val' not in locals() or 'y_val' not in locals(): |
| |
| val_batch_size = 16 |
| x_val = torch.randn(val_batch_size, input_dim, sequence_length, dtype=torch.float32).to(device) |
| y_val = torch.randint(0, 2, (val_batch_size,), dtype=torch.long).to(device) |
| print("Generated synthetic validation data for evaluation.") |
|
|
| |
| if 'z' in locals() and 'edge_index' in locals(): |
| z = z.to(device) |
| edge_index = edge_index.to(device) |
| else: |
| print("⚠️ Warning: Graph data (z, edge_index) not found. Skipping evaluation.") |
| |
| |
| pass |
|
|
| |
| |
| val_outputs = hybrid_model(x_val, edge_index, z) |
|
|
| |
| val_loss = F.binary_cross_entropy_with_logits(val_outputs.squeeze(-1), y_val.float()) |
|
|
| |
| |
| predicted_classes = (torch.sigmoid(val_outputs.squeeze(-1)) > 0.5).long() |
|
|
| |
| correct_predictions = (predicted_classes == y_val).sum().item() |
| accuracy = correct_predictions / val_batch_size |
|
|
| print(f'Validation Loss: {val_loss.item():.4f}, Validation Accuracy: {accuracy:.4f}') |
|
|
|
|
| |
| class DiracGraphConv(nn.Module): |
| def __init__(self, in_dim: int, out_dim: int, alpha: float = 1.0, bias: bool = True): |
| super().__init__() |
| self.lin = nn.Linear(in_dim, out_dim, bias=bias) |
| self.alpha = nn.Parameter(torch.tensor(alpha, dtype=torch.float32)) |
| self.bias_edge = nn.Parameter(torch.tensor(0.0, dtype=torch.float32)) |
|
|
| @staticmethod |
| def cosine_corr(z_i: torch.Tensor, z_j: torch.Tensor, eps: float = 1e-9) -> torch.Tensor: |
| num = (z_i * z_j).sum(dim=-1) |
| den = torch.clamp(z_i.norm(dim=-1) * z_j.norm(dim=-1), min=eps) |
| return num / den |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| N = x.size(0) |
| row, col = edge_index |
| corr = self.cosine_corr(z[row], z[col]) |
| logits = self.alpha * corr + self.bias_edge |
| device = x.device |
| E = row.size(0) |
| ones = torch.ones(E, device=device) |
| max_per_row = torch.full((N,), -1e9, device=device) |
| max_per_row = max_per_row.index_put((row,), logits, accumulate=False).scatter_reduce_(0, row, logits, reduce="amax") |
| logits_centered = logits - max_per_row[row] |
| exp_logits = torch.exp(logits_centered) |
| denom = torch.zeros(N, device=device).index_add_(0, row, exp_logits) |
| attn = exp_logits / (denom[row] + 1e-9) |
| deg = torch.zeros(N, device=device).index_add_(0, row, ones) |
| norm = 1.0 / torch.clamp(deg[row], min=1.0) |
| msgs = norm.unsqueeze(-1) * attn.unsqueeze(-1) * x[col] |
| out = torch.zeros_like(x).index_add_(0, row, msgs) |
| return self.lin(out) |
|
|
|
|
| |
| class GNNDiracRRF(nn.Module): |
| def __init__(self, in_dim: int, hidden_dim: int, out_dim: int, num_layers: int, z_dim: int, |
| alpha_attn: float = 1.0, dropout: float = 0.1): |
| super().__init__() |
| self.z_dim = z_dim |
| self.layers = nn.ModuleList() |
| self.layers.append(DiracGraphConv(in_dim, hidden_dim, alpha=alpha_attn)) |
| for _ in range(num_layers - 2): |
| self.layers.append(DiracGraphConv(hidden_dim, hidden_dim, alpha=alpha_attn)) |
| self.layers.append(DiracGraphConv(hidden_dim, out_dim, alpha=alpha_attn)) |
| self.dropout = nn.Dropout(dropout) |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| h = x |
| for i, layer in enumerate(self.layers): |
| h = layer(h, edge_index, z) |
| if i < len(self.layers) - 1: |
| h = F.gelu(h) |
| h = self.dropout(h) |
| return h |
|
|
|
|
| |
| class DiracGraphConv(nn.Module): |
| def __init__(self, in_dim: int, out_dim: int, alpha: float = 1.0, bias: bool = True): |
| super().__init__() |
| self.lin = nn.Linear(in_dim, out_dim, bias=bias) |
| self.alpha = nn.Parameter(torch.tensor(alpha, dtype=torch.float32)) |
| self.bias_edge = nn.Parameter(torch.tensor(0.0, dtype=torch.float32)) |
|
|
| @staticmethod |
| def cosine_corr(z_i: torch.Tensor, z_j: torch.Tensor, eps: float = 1e-9) -> torch.Tensor: |
| num = (z_i * z_i).sum(dim=-1) |
| den = torch.clamp(z_i.norm(dim=-1) * z_j.norm(dim=-1), min=eps) |
| return num / den |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| N = x.size(0) |
| row, col = edge_index |
| corr = self.cosine_corr(z[row], z[col]) |
| logits = self.alpha * corr + self.bias_edge |
| device = x.device |
| E = row.size(0) |
| ones = torch.ones(E, device=device) |
| max_per_row = torch.full((N,), -1e9, device=device) |
| max_per_row = max_per_row.index_put((row,), logits, accumulate=False).scatter_reduce_(0, row, logits, reduce="amax") |
| logits_centered = logits - max_per_row[row] |
| exp_logits = torch.exp(logits_centered) |
| denom = torch.zeros(N, device=device).index_add_(0, row, exp_logits) |
| attn = exp_logits / (denom[row] + 1e-9) |
| deg = torch.zeros(N, device=device).index_add_(0, row, ones) |
| norm = 1.0 / torch.clamp(deg[row], min=1.0) |
| msgs = norm.unsqueeze(-1) * attn.unsqueeze(-1) * x[col] |
| out = torch.zeros_like(x).index_add_(0, row, msgs) |
| return self.lin(out) |
|
|
|
|
| |
| class GNNDiracRRF(nn.Module): |
| def __init__(self, in_dim: int, hidden_dim: int, out_dim: int, num_layers: int, z_dim: int, |
| alpha_attn: float = 1.0, dropout: float = 0.1): |
| super().__init__() |
| self.z_dim = z_dim |
| self.layers = nn.ModuleList() |
| self.layers.append(DiracGraphConv(in_dim, hidden_dim, alpha=alpha_attn)) |
| for _ in range(num_layers - 2): |
| self.layers.append(DiracGraphConv(hidden_dim, hidden_dim, alpha=alpha_attn)) |
| self.layers.append(DiracGraphConv(hidden_dim, out_dim, alpha=alpha_attn)) |
| self.dropout = nn.Dropout(dropout) |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| h = x |
| for i, layer in enumerate(self.layers): |
| h = layer(h, edge_index, z) |
| if i < len(self.layers) - 1: |
| h = F.gelu(h) |
| h = self.dropout(h) |
| return h |
|
|
|
|
| |
| class SavantRRF_Gauge(nn.Module): |
| def __init__(self, input_dim, hidden_dim, output_dim): |
| super(SavantRRF_Gauge, self).__init__() |
| self.conv1 = nn.Conv1d(input_dim, 64, kernel_size=3, padding=1) |
| self.conv2 = nn.Conv1d(64, 128, kernel_size=3, padding=1) |
| self.conv3 = nn.Conv1d(128, 256, kernel_size=3, padding=1) |
| self.dropout = nn.Dropout(0.25) |
| |
| self.fc1 = nn.Linear(256*160, 512) |
| self.fc2 = nn.Linear(512, 256) |
| self.fc3 = nn.Linear(256, output_dim) |
|
|
| def forward(self, x): |
| x = F.relu(self.conv1(x)) |
| x = F.relu(self.conv2(x)) |
| x = F.relu(self.conv3(x)) |
| x = torch.flatten(x, 1) |
| x = self.dropout(x) |
| x = F.relu(self.fc1(x)) |
| x = F.relu(self.fc2(x)) |
| return torch.sigmoid(self.fc3(x)) |
|
|
|
|
| |
| class IcosahedralRRF(nn.Module): |
| def __init__(self, input_dim, hidden_dim, output_dim, gnn_num_layers=2, gnn_z_dim=16, gnn_alpha_attn=1.0, gnn_dropout=0.1): |
| super(IcosahedralRRF, self).__init__() |
| |
| self.nodes = nn.ModuleList([ |
| SavantRRF_Gauge(input_dim, hidden_dim, output_dim) for _ in range(12) |
| ]) |
| |
| self.ethical_core = nn.Linear(12 * output_dim, output_dim) |
|
|
| |
| |
| |
| |
| self.memory_map = GNNDiracRRF(in_dim=output_dim, |
| hidden_dim=hidden_dim, |
| out_dim=output_dim, |
| num_layers=gnn_num_layers, |
| z_dim=gnn_z_dim, |
| alpha_attn=gnn_alpha_attn, |
| dropout=gnn_dropout) |
|
|
|
|
| def forward(self, x, edge_index=None, z=None): |
| |
| outputs = [node(x) for node in self.nodes] |
| |
|
|
| |
| concat = torch.cat(outputs, dim=1) |
| regulated = torch.sigmoid(self.ethical_core(concat)) |
|
|
| |
| if edge_index is not None and z is not None: |
| |
| stacked_outputs = torch.stack(outputs, dim=1) |
|
|
| gnn_outputs_list = [] |
| for i in range(stacked_outputs.size(0)): |
| gnn_input_features_i = stacked_outputs[i] |
| edge_index_i = edge_index.to(x.device) |
| z_i = z.to(x.device) |
| gnn_output_i = self.memory_map(gnn_input_features_i, edge_index_i, z_i) |
| gnn_outputs_list.append(gnn_output_i) |
|
|
| gnn_outputs_stacked = torch.stack(gnn_outputs_list, dim=0) |
| aggregated_gnn_output = gnn_outputs_stacked.mean(dim=1) |
|
|
| return aggregated_gnn_output |
|
|
| else: |
| return regulated |
|
|
|
|
| |
| class DiracGraphConv(nn.Module): |
| def __init__(self, in_dim: int, out_dim: int, alpha: float = 1.0, bias: bool = True): |
| super().__init__() |
| self.lin = nn.Linear(in_dim, out_dim, bias=bias) |
| self.alpha = nn.Parameter(torch.tensor(alpha, dtype=torch.float32)) |
| self.bias_edge = nn.Parameter(torch.tensor(0.0, dtype=torch.float32)) |
|
|
| @staticmethod |
| def cosine_corr(z_i: torch.Tensor, z_j: torch.Tensor, eps: float = 1e-9) -> torch.Tensor: |
| num = (z_i * z_j).sum(dim=-1) |
| den = torch.clamp(z_i.norm(dim=-1) * z_j.norm(dim=-1), min=eps) |
| return num / den |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| N = x.size(0) |
| row, col = edge_index |
| |
| |
| |
| |
| |
| corr = self.cosine_corr(z[row], z[col]) |
| logits = self.alpha * corr + self.bias_edge |
| device = x.device |
| E = row.size(0) |
| ones = torch.ones(E, device=device) |
| |
| max_per_row = torch.full((N,), -1e9, device=device) |
| max_per_row = max_per_row.index_put((row,), logits, accumulate=False).scatter_reduce_(0, row, logits, reduce="amax") |
| logits_centered = logits - max_per_row[row] |
| exp_logits = torch.exp(logits_centered) |
| denom = torch.zeros(N, device=device).index_add_(0, row, exp_logits) |
| attn = exp_logits / (denom[row] + 1e-9) |
| deg = torch.zeros(N, device=device).index_add_(0, row, ones) |
| norm = 1.0 / torch.clamp(deg[row], min=1.0) |
| msgs = norm.unsqueeze(-1) * attn.unsqueeze(-1) * x[col] |
| out = torch.zeros_like(x).index_add_(0, row, msgs) |
| return self.lin(out) |
|
|
|
|
| |
| class GNNDiracRRF(nn.Module): |
| def __init__(self, in_dim: int, hidden_dim: int, out_dim: int, num_layers: int, z_dim: int, |
| alpha_attn: float = 1.0, dropout: float = 0.1): |
| super().__init__() |
| self.z_dim = z_dim |
| self.layers = nn.ModuleList() |
| |
| self.layers.append(DiracGraphConv(in_dim, hidden_dim, alpha=alpha_attn)) |
| for _ in range(num_layers - 2): |
| self.layers.append(DiracGraphConv(hidden_dim, hidden_dim, alpha=alpha_attn)) |
| self.layers.append(DiracGraphConv(hidden_dim, out_dim, alpha=alpha_attn)) |
| self.dropout = nn.Dropout(dropout) |
|
|
| def forward(self, x: torch.Tensor, edge_index: torch.Tensor, z: torch.Tensor) -> torch.Tensor: |
| h = x |
| for i, layer in enumerate(self.layers): |
| h = layer(h, edge_index, z) |
| if i < len(self.layers) - 1: |
| h = F.gelu(h) |
| h = self.dropout(h) |
| return h |
|
|
|
|
| |
| class RRF_Dataset(Dataset): |
| def __init__(self, strain, weights, seq_len=160): |
| self.seq_len = seq_len |
| self.strain = strain |
| self.weights = weights |
| print(f"Debug: RRF_Dataset __init__ - len(strain): {len(strain)}, seq_len: {self.seq_len}") |
| |
| if len(strain) >= seq_len: |
| self.n = len(strain) // seq_len |
| else: |
| self.n = 0 |
| print(f"Debug: RRF_Dataset __init__ - Calculated self.n: {self.n}") |
| |
| if self.n == 0: |
| raise ValueError(f"Strain data length ({len(strain)}) is less than sequence length ({seq_len}). Cannot create any samples.") |
|
|
|
|
| def __len__(self): |
| return self.n |
|
|
| def __getitem__(self, idx): |
| start = idx * self.seq_len |
| |
| x = self.strain[start:start+self.seq_len] |
|
|
| |
| w = np.mean(self.weights) |
|
|
| |
| |
| y = np.mean(x) * w |
|
|
| |
| |
| return torch.tensor(x).float().unsqueeze(0), torch.tensor(y).float() |
|
|
|
|
|
|
| def load_model_state(path, model_instance, map_location='cpu'): |
| '''Helper: load state_dict from path into model_instance (PyTorch).''' |
| state = torch.load(path, map_location=map_location) |
| if isinstance(state, dict) and ('state_dict' in state and isinstance(state['state_dict'], dict)): |
| state = state['state_dict'] |
| model_instance.load_state_dict(state) |
| return model_instance |
|
|