| 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 |
|
|
| |
| 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'] |
|
|
| |
| |
| |
| num_qubits = 4 |
| X_train_q = X_train[:, :num_qubits] |
| X_test_q = X_test[:, :num_qubits] |
|
|
| |
| 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.") |
|
|
| |
| |
| feature_map = ZFeatureMap(feature_dimension=num_qubits, reps=1) |
|
|
| |
| ansatz = RealAmplitudes(num_qubits, reps=1) |
|
|
| |
| qc = QuantumCircuit(num_qubits) |
| qc.compose(feature_map, inplace=True) |
| qc.compose(ansatz, inplace=True) |
|
|
| |
| 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 |
| ) |
|
|
| |
| 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() |
|
|
| |
| 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}") |
|
|
| |
| 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)") |