File size: 4,482 Bytes
0f755ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import numpy as np
import os
import time

# --- M1 OPTIMIZATIONS ---
os.environ["OMP_NUM_THREADS"] = "4"
os.environ["QISKIT_IN_PARALLEL"] = "TRUE"

from sklearn.svm import SVC
from sklearn.metrics import roc_auc_score
from qiskit.circuit import ParameterVector
from qiskit.circuit.library import ZZFeatureMap, RealAmplitudes
from qiskit_machine_learning.kernels import TrainableFidelityQuantumKernel
from qiskit_machine_learning.utils.loss_functions import SVCLoss
from qiskit_algorithms.optimizers import COBYLA
from qiskit_aer import AerSimulator

# --- CONFIGURATION ---
N_QUBITS = 8         
TRAIN_SIZE = 150     # Smaller subset for alignment (speed) now 150 for even faster
TEST_SIZE = 300
MAX_ITERS = 50       # Optimization steps

OUTPUT_DIR = "optimized_kernel_results"
if not os.path.exists(OUTPUT_DIR):
    os.makedirs(OUTPUT_DIR)

def main():
    print(f"πŸš€ Starting Quantum Kernel Optimization (QKA)...")
    print("   Goal: Calculate optimal kernel geometry instead of guessing.")

    # 1. LOAD DATA
    possible_paths = ['vG.0.1/qgan_data_optimized.npz', 'qgan_data_optimized.npz']
    data_path = next((p for p in possible_paths if os.path.exists(p)), None)
    if not data_path: 
        print("❌ Error: qgan_data_optimized.npz not found.")
        return

    data = np.load(data_path)
    X_train_full = data['X_train']
    y_train_full = data['y_train']
    X_test_full = data['X_test']
    y_test_full = data['y_test']

    # 2. SUBSAMPLE (Balanced)
    pos_idx = np.where(y_train_full == 1)[0]
    neg_idx = np.where(y_train_full == 0)[0]
    train_idx = np.concatenate([
        np.random.choice(pos_idx, TRAIN_SIZE // 2, replace=False),
        np.random.choice(neg_idx, TRAIN_SIZE // 2, replace=False)
    ])
    np.random.shuffle(train_idx)
    
    X_train = X_train_full[train_idx]
    y_train = y_train_full[train_idx]
    X_test = X_test_full[:TEST_SIZE]
    y_test = y_test_full[:TEST_SIZE]

    print(f"   πŸ“Š Training Alignment on {len(X_train)} samples...")

    # 3. DEFINE TRAINABLE FEATURE MAP
    # Circuit = Data Encoding (ZZ) + Trainable Rotation (RealAmplitudes)
    # This acts as a "Quantum Lens" that the optimizer focuses.
    print("   βš›οΈ  Initializing Trainable Kernel...")
    
    fm = ZZFeatureMap(N_QUBITS, reps=2, entanglement='linear')
    ansatz = RealAmplitudes(N_QUBITS, reps=1)
    
    combined_circuit = fm.compose(ansatz)
    
    # FIX: Only pass the training_parameters. 
    # The class automatically infers that the other params (fm.parameters) are data inputs.
    quant_kernel = TrainableFidelityQuantumKernel(
        feature_map=combined_circuit,
        training_parameters=ansatz.parameters
    )

    # 4. OPTIMIZE KERNEL (ALIGNMENT)
    print("   🧠 Optimizing Kernel Geometry (COBYLA)...")
    
    # SVCLoss minimizes the classification error of the kernel
    loss_func = SVCLoss(C=1.0, gamma='auto')
    optimizer = COBYLA(maxiter=MAX_ITERS)
    
    from qiskit_machine_learning.kernels.algorithms import QuantumKernelTrainer
    
    trainer = QuantumKernelTrainer(
        quantum_kernel=quant_kernel, 
        loss=loss_func, 
        optimizer=optimizer,
        initial_point=[0.1] * len(ansatz.parameters)
    )
    
    start_time = time.time()
    
    # Train the Kernel parameters
    results = trainer.fit(X_train, y_train)
    
    print(f"   βœ… Optimization Complete in {time.time() - start_time:.1f}s")
    print(f"   🎯 Final Loss: {results.optimal_value:.4f}")
    
    # 5. RUN FINAL SVC WITH OPTIMIZED KERNEL
    print("   πŸ† Training Final SVM with Optimized Kernel...")
    
    optimized_kernel = results.quantum_kernel
    
    # We use the optimized kernel to fit the full SVM
    qsvc = SVC(kernel=optimized_kernel.evaluate, probability=True)
    qsvc.fit(X_train, y_train)
    
    # Evaluate
    test_probs = qsvc.predict_proba(X_test)[:, 1]
    test_auc = roc_auc_score(y_test, test_probs)
    
    print("\n" + "="*40)
    print("πŸš€ OPTIMIZED QUANTUM KERNEL RESULTS")
    print("="*40)
    print(f"βœ… Test AUC:      {test_auc:.4f}")
    print(f"πŸ“‰ Linear Base:   0.7500")
    print("="*40)
    
    # Check if we beat the baseline
    if test_auc > 0.75:
        print("πŸŽ‰ SUCCESS: Optimization found a better topology than brute force.")
        np.save(f"{OUTPUT_DIR}/optimized_weights.npy", results.optimal_point)
    else:
        print("πŸ”Έ Result: Optimization matched or slightly underperformed baseline.")

if __name__ == "__main__":
    main()