clique / src /old /3_simple_lrmc_integration.py
qingy2024's picture
Upload folder using huggingface_hub
bf620c6 verified
import torch
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid
from torch_geometric.nn import GCNConv
import torch_geometric.transforms as T
import numpy as np
import argparse
import networkx as nx
class GCN(torch.nn.Module):
def __init__(self, in_channels, hidden_channels, out_channels, dropout=0.5):
super().__init__()
self.conv1 = GCNConv(in_channels, hidden_channels)
self.conv2 = GCNConv(hidden_channels, out_channels)
self.dropout = dropout
def forward(self, x, edge_index):
x = self.conv1(x, edge_index)
x = F.relu(x)
x = F.dropout(x, p=self.dropout, training=self.training)
x = self.conv2(x, edge_index)
return x
def evaluate_integration_methods(dataset_name='Cora'):
"""Test different ways to integrate the L-RMC component"""
# Load data
dataset = Planetoid(root="./data", name=dataset_name, transform=T.NormalizeFeatures())
data = dataset[0]
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
data = data.to(device)
print(f"Total number of edges: {data.num_edges}")
print(f"Total number of nodes: {data.num_nodes}")
# L-RMC component (convert from 1-indexed Java IDs back to PyG indices)
lrmc_nodes_java = "1680 2883 901 617 1578 1103"
# Load the saved remap dictionary from the benchmark script
import pickle
remap_filename = f"{dataset_name.lower()}_remap.pkl"
with open(remap_filename, "rb") as f:
remap_data = pickle.load(f)
nodes_sorted = remap_data['nodes_sorted']
# Convert Java 1-indexed IDs back to PyG indices
lrmc_nodes_java_ids = [int(x) for x in lrmc_nodes_java.split()]
lrmc_nodes = [nodes_sorted[j-1] for j in lrmc_nodes_java_ids]
print(f"Loaded {len(lrmc_nodes)} L-RMC nodes with correct ID mapping")
print(f"Java IDs: {lrmc_nodes_java_ids[:5]}...")
print(f"PyG IDs: {lrmc_nodes[:5]}...")
# Verify connectivity and S(C) on correct nodes
lrmc_set = set(lrmc_nodes)
print("\nVerification:")
print(f"L-RMC component size: {len(lrmc_set)}")
# Check connectivity within the component
G = nx.Graph()
edges = data.edge_index.t().tolist()
G.add_edges_from(edges)
# Create subgraph of L-RMC component
lrmc_subgraph = G.subgraph(lrmc_nodes)
internal_edges = lrmc_subgraph.number_of_edges()
possible_edges = len(lrmc_nodes) * (len(lrmc_nodes) - 1) // 2
density = internal_edges / possible_edges if possible_edges > 0 else 0
print(f"Internal edges in L-RMC: {internal_edges}")
print(f"Possible edges: {possible_edges}")
print(f"Density: {density:.4f}")
# Calculate S(C) - sum of degrees within component
s_c = 0
for node in lrmc_nodes:
neighbors = set(G.neighbors(node))
internal_neighbors = neighbors & lrmc_set
s_c += len(internal_neighbors)
print(f"S(C): {s_c}")
print(f"Average internal degree: {s_c / len(lrmc_nodes):.2f}")
integration_methods = {
"baseline": "no_modification",
"binary_feature": "add_binary_feature",
"feature_scaling": "scale_features_2x",
"feature_scaling_strong": "scale_features_5x",
"feature_boost": "add_constant_to_features",
"edge_weights": "boost_internal_edges"
}
results = {}
for method_name, method_type in integration_methods.items():
print(f"\n{'='*50}")
print(f"Testing: {method_name}")
print(f"{'='*50}")
accuracies = []
for seed in range(42, 60): # 5 runs
torch.manual_seed(seed)
np.random.seed(seed)
# Apply integration method
if method_type == "no_modification":
x_input = data.x
edge_index = data.edge_index
elif method_type == "add_binary_feature":
component_feature = torch.zeros(data.num_nodes, 1, device=device)
component_feature[lrmc_nodes] = 1.0
x_input = torch.cat([data.x, component_feature], dim=1)
edge_index = data.edge_index
elif method_type == "scale_features_2x":
x_input = data.x.clone()
x_input[lrmc_nodes] *= 2.0
edge_index = data.edge_index
elif method_type == "scale_features_5x":
x_input = data.x.clone()
x_input[lrmc_nodes] *= 5.0
edge_index = data.edge_index
elif method_type == "add_constant_to_features":
x_input = data.x.clone()
x_input[lrmc_nodes] += 1.0 # Add constant to all features
edge_index = data.edge_index
elif method_type == "boost_internal_edges":
x_input = data.x
edge_index = data.edge_index.clone()
# Create additional "virtual" edges within the component
lrmc_set = set(lrmc_nodes)
virtual_edges = []
for i, u in enumerate(lrmc_nodes):
for v in lrmc_nodes[i+1:]:
# Add multiple copies of internal edges to boost them
for _ in range(3): # Triple the internal edges
virtual_edges.extend([[u, v], [v, u]])
if virtual_edges:
virtual_edge_tensor = torch.tensor(virtual_edges, device=device).t()
edge_index = torch.cat([edge_index, virtual_edge_tensor], dim=1)
# Train model
model = GCN(x_input.shape[1], 16, dataset.num_classes).to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
best_val_acc = 0
test_acc_at_best_val = 0
for epoch in range(200):
model.train()
optimizer.zero_grad()
out = model(x_input, edge_index)
loss = F.cross_entropy(out[data.train_mask], data.y[data.train_mask])
loss.backward()
optimizer.step()
if epoch % 20 == 0:
model.eval()
with torch.no_grad():
pred = model(x_input, edge_index).argmax(dim=1)
val_correct = pred[data.val_mask] == data.y[data.val_mask]
val_acc = int(val_correct.sum()) / int(data.val_mask.sum())
if val_acc > best_val_acc:
best_val_acc = val_acc
test_correct = pred[data.test_mask] == data.y[data.test_mask]
test_acc_at_best_val = int(test_correct.sum()) / int(data.test_mask.sum())
accuracies.append(test_acc_at_best_val)
print(f" Seed {seed}: {test_acc_at_best_val:.4f}")
mean_acc = np.mean(accuracies)
std_acc = np.std(accuracies)
results[method_name] = (mean_acc, std_acc)
print(f" Mean ± Std: {mean_acc:.4f} ± {std_acc:.4f}")
# Final summary
print(f"\n{'='*60}")
print("FINAL INTEGRATION METHOD COMPARISON")
print(f"{'='*60}")
print(f"{'Method':<20} | {'Mean Accuracy':<15} | {'Std Dev':<10} | {'vs Baseline'}")
print("-" * 70)
baseline_acc = results["baseline"][0]
for method, (mean_acc, std_acc) in results.items():
improvement = mean_acc - baseline_acc
print(f"{method:<20} | {mean_acc:<15.4f} | {std_acc:<10.4f} | {improvement:+.4f}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Evaluate L-RMC integration methods on a specified dataset')
parser.add_argument('--dataset', type=str, default='Cora',
choices=['Cora', 'CiteSeer', 'PubMed'],
help='Dataset to use (default: Cora)')
args = parser.parse_args()
evaluate_integration_methods(args.dataset)