diff --git a/raw_bellman_ford/README.md b/raw_bellman_ford/README.md new file mode 100644 index 0000000..cee1674 --- /dev/null +++ b/raw_bellman_ford/README.md @@ -0,0 +1,44 @@ +# BellmanFordLayerModified + +`BellmanFordLayer` - это PyTorch-слой, предоставляющий результаты алгоритма Беллмана-Форда для анализа графовых данных. Он возвращает матрицу расстояний и матрицу предшественников, которые могут быть использованы для поиска кратчайших путей в графе. Также этот слой определяет наличие отрицательных циклов в графе. + +`BellmanFordLayerModified` - это PyTorch слой, реализующий модифицированный алгоритм Беллмана-Форда для анализа свойств графов и извлечения признаков из графовой структуры. Этот слой может использоваться в задачах графового машинного обучения, таких как предсказание путей и анализ графовых структур. + +## Использование + +```python +import torch +from layers.bellman_ford_modified import BellmanFordLayerModified + +# Инициализация слоя с указанием количества узлов и числа признаков +num_nodes = 4 +num_features = 5 +bellman_ford_layer = BellmanFordLayerModified(num_nodes, num_features) + +# Определение матрицы смежности графа и начального узла +adj_matrix = torch.tensor([[0, 2, float('inf'), 1], + [float('inf'), 0, -1, float('inf')], + [float('inf'), float('inf'), 0, -2], + [float('inf'), float('inf'), float('inf'), 0]]) +source_node = 0 + +# Вычисление признаков графа, диаметра и эксцентриситета +node_features, diameter, eccentricity = bellman_ford_layer(adj_matrix, source_node) + +print("Node Features:") +print(node_features) +print("Graph Diameter:", diameter) +print("Graph Eccentricity:", eccentricity) +``` + +## Параметры слоя + +- `num_nodes`: Количество узлов в графе. +- `num_features`: Количество признаков, извлекаемых из графа. +- `edge_weights`: Веса ребер между узлами (обучаемые параметры). +- `node_embedding`: Вложение узлов для извлечения признаков. + +## Применение: + +- BellmanFordLayer полезен, когда вам нужны результаты алгоритма Беллмана-Форда для выполнения других операций или анализа графа. +- BellmanFordLayerModified полезен, когда вас помимо путей интересуют дополнительные характеристики графа, такие как диаметр и эксцентриситет. diff --git a/raw_bellman_ford/layers/bellman_ford_modified.py b/raw_bellman_ford/layers/bellman_ford_modified.py new file mode 100644 index 0000000..8f4bdc4 --- /dev/null +++ b/raw_bellman_ford/layers/bellman_ford_modified.py @@ -0,0 +1,97 @@ +import torch +import torch.nn as nn + +class BellmanFordLayerModified(nn.Module): + def __init__(self, num_nodes, num_features): + super(BellmanFordLayerModified, self).__init__() + self.num_nodes = num_nodes + self.num_features = num_features + + self.edge_weights = nn.Parameter(torch.rand(num_nodes, num_nodes)) + self.node_embedding = nn.Embedding(num_nodes, num_features) + + def forward(self, adj_matrix, source_node): + distances = torch.full((self.num_nodes,), float('inf')) + predecessors = torch.full((self.num_nodes,), -1) + distances[source_node] = 0 + + for _ in range(self.num_nodes - 1): + for s in range(self.num_nodes): + for d in range(self.num_nodes): + if s != d and adj_matrix[s][d] != float('inf'): + if distances[s] + adj_matrix[s][d] < distances[d]: + distances[d] = distances[s] + adj_matrix[s][d] + predecessors[d] = s + + graph_diameter = torch.max(distances).item() + graph_eccentricity = torch.max(distances[source_node]).item() + + node_features = self.node_embedding(torch.arange(self.num_nodes)) + node_features = torch.cat([node_features, distances.unsqueeze(1)], dim=1) + + return node_features, graph_diameter, graph_eccentricity + +if __name__ == "__main__": + num_nodes_1 = 4 + adj_matrix_1 = torch.tensor([[0, 2, float('inf'), 1], + [float('inf'), 0, -1, float('inf')], + [float('inf'), float('inf'), 0, -2], + [float('inf'), float('inf'), float('inf'), 0]]) + source_node_1 = 0 + + bellman_ford_layer_1 = BellmanFordLayerModified(num_nodes_1, num_features=5) + node_features_1, diameter_1, eccentricity_1 = bellman_ford_layer_1(adj_matrix_1, source_node_1) + + print("Example 1:") + print("Node Features:") + print(node_features_1) + print("Graph Diameter:", diameter_1) + print("Graph Eccentricity:", eccentricity_1) + + num_nodes_2 = 4 + adj_matrix_2 = torch.tensor([[0, 2, 1, float('inf')], + [float('inf'), 0, -1, float('inf')], + [float('inf'), float('inf'), 0, -2], + [float('inf'), float('inf'), float('inf'), 0]]) + source_node_2 = 0 + + bellman_ford_layer_2 = BellmanFordLayerModified(num_nodes_2, num_features=5) + node_features_2, diameter_2, eccentricity_2 = bellman_ford_layer_2(adj_matrix_2, source_node_2) + + print("\nExample 2:") + print("Node Features:") + print(node_features_2) + print("Graph Diameter:", diameter_2) + print("Graph Eccentricity:", eccentricity_2) + + num_nodes_3 = 4 + adj_matrix_3 = torch.tensor([[0, 2, 1, 3], + [-1, 0, -1, 4], + [5, 2, 0, -2], + [2, 3, 1, 0]]) + source_node_3 = 0 + + bellman_ford_layer_3 = BellmanFordLayerModified(num_nodes_3, num_features=5) + node_features_3, diameter_3, eccentricity_3 = bellman_ford_layer_3(adj_matrix_3, source_node_3) + + print("\nExample 3:") + print("Node Features:") + print(node_features_3) + print("Graph Diameter:", diameter_3) + print("Graph Eccentricity:", eccentricity_3) + + num_nodes_4 = 4 + adj_matrix_4 = torch.tensor([[0, 2, 0, 1], + [0, 0, 0, 0], + [0, 0, 0, 2], + [0, 0, 0, 0]]) + source_node_4 = 0 + + bellman_ford_layer_4 = BellmanFordLayerModified(num_nodes_4, num_features=5) + node_features_4, diameter_4, eccentricity_4 = bellman_ford_layer_4(adj_matrix_4, source_node_4) + + print("\nExample 4:") + print("Node Features:") + print(node_features_4) + print("Graph Diameter:", diameter_4) + print("Graph Eccentricity:", eccentricity_4) diff --git a/raw_bellman_ford/layers/bellman_ford_orig.py b/raw_bellman_ford/layers/bellman_ford_orig.py new file mode 100644 index 0000000..32deba2 --- /dev/null +++ b/raw_bellman_ford/layers/bellman_ford_orig.py @@ -0,0 +1,73 @@ +import torch +import torch.nn as nn + +class BellmanFordLayer(nn.Module): + def __init__(self, num_nodes): + super(BellmanFordLayer, self).__init__() + self.num_nodes = num_nodes + + def forward(self, adj_matrix, source_node): + distances = torch.full((self.num_nodes, self.num_nodes), float('inf')) + predecessors = torch.zeros((self.num_nodes, self.num_nodes), dtype=torch.long) + + distances[source_node, 0] = 0 + + for i in range(1, self.num_nodes): + for u in range(self.num_nodes): + for v in range(self.num_nodes): + w = adj_matrix[u, v] + if distances[u, i - 1] + w < distances[v, i]: + distances[v, i] = distances[u, i - 1] + w + predecessors[v, i] = u + + has_negative_cycle = False + for u in range(self.num_nodes): + for v in range(self.num_nodes): + w = adj_matrix[u, v] + if distances[u, self.num_nodes - 1] + w < distances[v, self.num_nodes - 1]: + has_negative_cycle = True + break + if has_negative_cycle: + break + + return distances, predecessors, has_negative_cycle + +if __name__ == "__main__": + num_nodes = 4 + source_node = 0 + + adj_matrix1 = torch.tensor([[0, 2, 1, float('inf')], + [float('inf'), 0, -1, float('inf')], + [float('inf'), float('inf'), 0, -2], + [float('inf'), float('inf'), float('inf'), 0]]) + + adj_matrix2 = torch.tensor([[0, 2, float('inf'), 1], + [1, 0, -1, float('inf')], + [float('inf'), float('inf'), 0, -2], + [float('inf'), 1, float('inf'), 0]]) + + adj_matrix3 = torch.tensor([[0, 2, 1, float('inf')], + [float('inf'), 0, -1, float('inf')], + [3, float('inf'), 0, -2], + [float('inf'), 1, float('inf'), 0]]) + + bellman_ford_layer = BellmanFordLayer(num_nodes) + + distances1, predecessors1, has_negative_cycle1 = bellman_ford_layer(adj_matrix1, source_node) + distances2, predecessors2, has_negative_cycle2 = bellman_ford_layer(adj_matrix2, source_node) + distances3, predecessors3, has_negative_cycle3 = bellman_ford_layer(adj_matrix3, source_node) + + if has_negative_cycle1: + print("Example 1: The graph contains a negative weight cycle") + else: + print("Example 1: Shortest distances from the source node:", distances1[source_node, -1]) + + if has_negative_cycle2: + print("Example 2: The graph contains a negative weight cycle") + else: + print("Example 2: Shortest distances from the source node:", distances2[source_node, -1]) + + if has_negative_cycle3: + print("Example 3: The graph contains a negative weight cycle") + else: + print("Example 3: Shortest distances from the source node:", distances3[source_node, -1]) diff --git a/raw_bellman_ford/node_classification_example.py b/raw_bellman_ford/node_classification_example.py new file mode 100644 index 0000000..c449da6 --- /dev/null +++ b/raw_bellman_ford/node_classification_example.py @@ -0,0 +1,62 @@ +import torch +import torch.nn as nn +import torch.optim as optim +from layers.bellman_ford_orig import BellmanFordLayer + +class NodeClassificationGNN(nn.Module): + def __init__(self, num_nodes, num_features, num_classes): + super(NodeClassificationGNN, self).__init__() + self.num_nodes = num_nodes + self.num_features = num_features + self.num_classes = num_classes + + self.bellman_ford_layer = BellmanFordLayer(num_nodes) + self.node_embedding = nn.Embedding(num_nodes, num_features) + self.fc = nn.Linear(num_features + num_nodes, num_classes) + + def forward(self, adj_matrix, source_node): + distances, predecessors, has_negative_cycle = self.bellman_ford_layer(adj_matrix, source_node) + + node_features = self.node_embedding(torch.arange(self.num_nodes)) + node_features = torch.cat([node_features, distances], dim=1) + + output = self.fc(node_features) + + return output, has_negative_cycle + +if __name__ == "__main__": + num_nodes = 4 + source_node = 0 + + adj_matrix = torch.tensor([[0, 2, 0, 1], + [0, 0, -1, 0], + [0, 0, 0, -2], + [0, 0, 0, 0]]) + + gnn_model = NodeClassificationGNN(num_nodes, num_features=5, num_classes=2) + + criterion = nn.CrossEntropyLoss() + optimizer = optim.Adam(gnn_model.parameters(), lr=0.001) + + num_epochs = 100 + for epoch in range(num_epochs): + optimizer.zero_grad() + output, has_negative_cycle = gnn_model(adj_matrix, source_node) + + labels = torch.tensor([0, 1, 0, 1], dtype=torch.long) + + loss = criterion(output, labels) + loss.backward() + optimizer.step() + + print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}, Negative Cycle: {has_negative_cycle}') + + test_adj_matrix = torch.tensor([[0, 1, 1, 0], + [0, 0, 0, 1], + [1, 0, 0, 0], + [0, 0, 1, 0]]) + + test_source_node = 0 + + test_output, has_negative_cycle = gnn_model(test_adj_matrix, test_source_node) + print("Test Output:", test_output, "Negative Cycle:", has_negative_cycle) diff --git a/raw_bellman_ford/predict_gnn_example.py b/raw_bellman_ford/predict_gnn_example.py new file mode 100644 index 0000000..ca26304 --- /dev/null +++ b/raw_bellman_ford/predict_gnn_example.py @@ -0,0 +1,57 @@ +import torch +import torch.nn as nn +import torch.optim as optim +from layers.bellman_ford_modified import BellmanFordLayerModified + +class GraphPathPredictionModel(nn.Module): + def __init__(self, num_nodes, num_features, hidden_dim): + super(GraphPathPredictionModel, self).__init__() + + self.bellman_ford_layer = BellmanFordLayerModified(num_nodes, num_features) + self.linear = nn.Linear(num_features + 1, hidden_dim) + + def forward(self, adj_matrix, source_node): + node_features, _, _ = self.bellman_ford_layer(adj_matrix, source_node) + + predictions = self.linear(node_features) + + return predictions + +if __name__ == "__main__": + num_nodes = 6 + num_features = 5 + hidden_dim = 2 + + adj_matrix = torch.tensor([[0, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0]]) + source_node = 0 + + model = GraphPathPredictionModel(num_nodes, num_features, hidden_dim) + + criterion = nn.BCEWithLogitsLoss() + optimizer = optim.SGD(model.parameters(), lr=0.1) + + labels = torch.tensor([1, 1, 1, 1, 0, 0], dtype=torch.float32).view(-1, 1).repeat(1, 2) + + num_epochs = 1000 + for epoch in range(num_epochs): + predictions = model(adj_matrix, source_node) + + predictions = torch.sigmoid(predictions) + + loss = criterion(predictions, labels) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}') + + with torch.no_grad(): + predictions = model(adj_matrix, source_node) + predicted_labels = (predictions > 0.5).type(torch.float32) + print("Predicted Labels:", predicted_labels) diff --git a/raw_bellman_ford/simple_gnn_example.py b/raw_bellman_ford/simple_gnn_example.py new file mode 100644 index 0000000..d9bfccd --- /dev/null +++ b/raw_bellman_ford/simple_gnn_example.py @@ -0,0 +1,73 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +import numpy as np +from layers.bellman_ford_orig import BellmanFordLayer + +class GNNWithBellmanFord(nn.Module): + def __init__(self, num_nodes, num_features, num_classes): + super(GNNWithBellmanFord, self).__init__() + self.num_nodes = num_nodes + self.num_features = num_features + self.num_classes = num_classes + + self.bellman_ford_layer = BellmanFordLayer(num_nodes) + self.node_embedding = nn.Embedding(num_nodes, num_features) + self.fc = nn.Linear(num_features + num_nodes, num_classes) + + def forward(self, adj_matrix, source_node): + distances, predecessors, has_negative_cycle = self.bellman_ford_layer(adj_matrix, source_node) + + node_features = self.node_embedding(torch.arange(self.num_nodes)) + node_features = torch.cat([node_features, distances], dim=1) + + output = self.fc(node_features) + + return output, has_negative_cycle + +if __name__ == "__main__": + + num_nodes = 4 + adj_matrix = torch.tensor([[0, 2, float('inf'), 1], + [float('inf'), 0, -1, float('inf')], + [float('inf'), float('inf'), 0, -2], + [float('inf'), float('inf'), float('inf'), 0]]) + source_node = 0 + + gnn_model = GNNWithBellmanFord(num_nodes, num_features=5, num_classes=2) + output, has_negative_cycle = gnn_model(adj_matrix, source_node) + + if has_negative_cycle: + print("Example 1: The graph contains a negative weight cycle") + else: + print("Example 1: GNN output:", output) + + num_nodes = 5 + adj_matrix = torch.tensor([[0, 1, 0, 0, 0], + [1, 0, 0, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 1, 0, 1], + [0, 0, 0, 1, 0]]) + source_node = 2 + + gnn_model = GNNWithBellmanFord(num_nodes, num_features=4, num_classes=3) + output, has_negative_cycle = gnn_model(adj_matrix, source_node) + + if has_negative_cycle: + print("Example 2: The graph contains a negative weight cycle") + else: + print("Example 2: GNN output:", output) + + num_nodes = 3 + adj_matrix = torch.tensor([[0, 1, 0], + [0, 0, 1], + [0, 0, 0]]) + source_node = 0 + + gnn_model = GNNWithBellmanFord(num_nodes, num_features=4, num_classes=2) + output, has_negative_cycle = gnn_model(adj_matrix, source_node) + + if has_negative_cycle: + print("Example 3: The graph contains a negative weight cycle") + else: + print("Example 3: GNN output:", output)