import numpy as np import torch import torch.nn as nn import torch.optim as optim from qiskit import QuantumCircuit from qiskit.circuit.library import RealAmplitudes, ZFeatureMap from qiskit_aer import AerSimulator from qiskit_machine_learning.connectors import TorchConnector from qiskit_machine_learning.neural_networks import SamplerQNN from sklearn.metrics import roc_auc_score # 1. Load Data (The Optimized Set from Phase VI) print("Loading validated data...") data = np.load('qgan_data_optimized.npz') X_train = data['X_train'] X_test = data['X_test'] y_train = data['y_train'] y_test = data['y_test'] # 2. Select Top 4 Features Only # Quantum hardware is small. We take the 4 most important physics signals. # (Beta_94, Beta_93, Beta_91, Beta_82 based on your previous output) num_qubits = 4 X_train_q = X_train[:, :num_qubits] X_test_q = X_test[:, :num_qubits] # Convert to PyTorch Tensors X_train_tensor = torch.tensor(X_train_q, dtype=torch.float32) y_train_tensor = torch.tensor(y_train, dtype=torch.float32).reshape(-1, 1) X_test_tensor = torch.tensor(X_test_q, dtype=torch.float32) print(f"Training Quantum Model on {len(X_train)} shots using {num_qubits} Qubits.") # 3. Build the Quantum Circuit # A. Feature Map: Encodes classical data into quantum states (Rotation Z) feature_map = ZFeatureMap(feature_dimension=num_qubits, reps=1) # B. Ansatz: The "Neural Network" weights (Entanglement + Rotations) ansatz = RealAmplitudes(num_qubits, reps=1) # C. Combine qc = QuantumCircuit(num_qubits) qc.compose(feature_map, inplace=True) qc.compose(ansatz, inplace=True) # 4. Define the Quantum Neural Network qnn = SamplerQNN( circuit=qc, input_params=feature_map.parameters, weight_params=ansatz.parameters, interpret=lambda x: x % 2, # Map quantum measurements to 0 or 1 output_shape=2 ) # 5. Hybrid PyTorch Module class HybridQNN(nn.Module): def __init__(self): super().__init__() self.qnn = TorchConnector(qnn) # Classical post-processing (Weighted sum of quantum output) self.fc = nn.Linear(1, 1) self.sigmoid = nn.Sigmoid() def forward(self, x): # Scale input to [0, 2pi] for quantum rotation x_scaled = x * torch.pi x_q = self.qnn(x_scaled) # We look at probability of state '1' (Disruption) x_out = self.fc(x_q[:, 1:2]) return self.sigmoid(x_out) model = HybridQNN() optimizer = optim.Adam(model.parameters(), lr=0.01) loss_func = nn.BCELoss() # 6. Training Loop epochs = 15 # Kept short for demonstration speed loss_list = [] print(f"\nšŸš€ Starting Hybrid Training ({epochs} epochs)...") model.train() for epoch in range(epochs): optimizer.zero_grad() # Forward pass output = model(X_train_tensor) loss = loss_func(output, y_train_tensor) # Backward pass loss.backward() optimizer.step() loss_list.append(loss.item()) print(f" Epoch {epoch+1}/{epochs} | Loss: {loss.item():.4f}") # 7. Evaluation print("\nEvaluating Quantum Model...") model.eval() with torch.no_grad(): test_preds = model(X_test_tensor).numpy() auc = roc_auc_score(y_test, test_preds) print("="*40) print(f"šŸ”¬ QUANTUM MODEL RESULTS") print("="*40) print(f"āœ… QNN AUC Score: {auc:.4f}") print(f"šŸ“Š Classical Benchmark to Beat: 0.8951") print("="*40) if auc > 0.8951: print("šŸ† RESULT: QUANTUM ADVANTAGE ACHIEVED (on this dataset)") elif auc > 0.85: print("🄈 RESULT: Competitive Performance (Close to Classical)") else: print("āš ļø RESULT: Underperformed (Expected for simple NISQ circuits)")