@@ -910,6 +910,255 @@ print(f'QSVC accuracy: {accuracy * 100:.2f}%')
910910
911911
912912
913+ !split
914+ ===== Credit data classification =====
915+
916+ We demonstrate a Quantum Support Vector Machine (QSVM) using PennyLane
917+ to classify synthetic credit data (good vs. bad credit). We generate a
918+ small dataset of financial features (e.g. income, debt ratio, age),
919+ encode them into quantum states via angle embedding, compute a quantum
920+ kernel (state overlap) matrix, and train a classical SVM using
921+ scikit-learn. Finally, we evaluate accuracy, precision, and
922+ recall. PennyLane’s default.qubit simulator is used throughout, and
923+ the code is heavily commented for clarity.
924+
925+
926+ !split
927+ ===== Synthetic Credit Data =====
928+
929+
930+ We create a toy dataset with three features per sample: income (in
931+ thousands), debt ratio (fraction of income), and age. Good-credit and
932+ bad-credit samples are drawn from different Gaussian clusters for
933+ illustration. After generation we scale features into suitable ranges
934+ (for example $[0,1]$ or $[0,\pi]$) before encoding into qubits.
935+
936+ !bc pycod
937+ import numpy as np
938+
939+ # Set random seed for reproducibility
940+ np.random.seed(42)
941+
942+ # Number of samples per class
943+ N_per = 50
944+
945+ # Define Gaussian cluster means and covariances for two classes
946+ mean_good = np.array([80, 0.3, 45]) # High income, low debt ratio, middle age
947+ cov_good = np.diag([50, 0.01, 20]) # Some variation
948+
949+ mean_bad = np.array([40, 0.7, 30]) # Low income, high debt ratio, younger
950+ cov_bad = np.diag([30, 0.02, 20])
951+
952+ # Sample synthetic data from multivariate Gaussians
953+ good_data = np.random.multivariate_normal(mean_good, cov_good, size=N_per)
954+ bad_data = np.random.multivariate_normal(mean_bad, cov_bad, size=N_per)
955+
956+ # Combine data and create labels (1 = good credit, 0 = bad credit)
957+ data = np.vstack([good_data, bad_data])
958+ labels = np.array([1]*N_per + [0]*N_per)
959+
960+ # Clip unrealistic values (e.g. negative values or ratios >1)
961+ data[:,0] = np.clip(data[:,0], 20, 120) # Income [20k, 120k]
962+ data[:,1] = np.clip(data[:,1], 0, 1) # Debt ratio [0, 1]
963+ data[:,2] = np.clip(data[:,2], 18, 70) # Age [18, 70]
964+
965+ # Normalize and scale features for quantum encoding
966+ # We scale to [0, pi] range to use as rotation angles
967+ income_scaled = (data[:,0] - 20) / 100 # ~[0,1]
968+ debt_scaled = data[:,1] # already [0,1]
969+ age_scaled = (data[:,2] - 18) / 52 # ~[0,1]
970+
971+ # Stack and multiply by pi for AngleEmbedding
972+ X = np.vstack([income_scaled, debt_scaled, age_scaled]).T * np.pi
973+
974+ # Split into train/test sets
975+ from sklearn.model_selection import train_test_split
976+ X_train, X_test, y_train, y_test = train_test_split(
977+ X, labels, test_size=0.2, stratify=labels, random_state=42
978+ )
979+
980+ print("Training samples:", X_train.shape, "Test samples:", X_test.shape)
981+ !ec
982+
983+ Explanation: We drew two clusters (good vs. bad credit) with distinct
984+ means, then clipped and scaled features into the $[0,\pi]$ range. This makes
985+ them suitable as rotation angles in an angle-embedding circuit. We
986+ split the data into training and test sets (80/20 split).
987+
988+ !split
989+ ===== Quantum Feature Encoding and Kernel Circuit =====
990+
991+
992+
993+
994+
995+ Each feature vector is encoded into a quantum state. We use
996+ PennyLane’s AngleEmbedding: each feature is used as an angle for a
997+ single-qubit rotation. For simplicity we use three qubits (one per
998+ feature) and rotation on the $y$-axis. Our quantum kernel is then
999+ defined by the state fidelity (overlap) between two encoded feature
1000+ vectors. Concretely, we build a QNode that encodes one data point,
1001+ then applies the inverse encoding of another, and measures the
1002+ probability of the all-zero state. This probability equals the squared
1003+ overlap between the two encoded states .
1004+
1005+ !bc pycod
1006+ import pennylane as qml
1007+ from pennylane import numpy as pnp
1008+
1009+ # Number of qubits = number of features
1010+ num_qubits = 3
1011+ dev = qml.device('default.qubit', wires=num_qubits)
1012+
1013+ @qml.qnode(dev)
1014+ def kernel_circuit(x1, x2):
1015+ """Quantum kernel circuit: encodes x1, then uncomputes x2."""
1016+ # Encode first sample
1017+ qml.templates.AngleEmbedding(features=x1, wires=range(num_qubits), rotation='Y')
1018+ # Apply the inverse (adjoint) embedding of second sample
1019+ qml.adjoint(qml.templates.AngleEmbedding)(features=x2, wires=range(num_qubits), rotation='Y')
1020+ # Measure probability of |0...0> state
1021+ return qml.probs(wires=range(num_qubits))
1022+
1023+ # Define kernel function: probability of all-zero outcome = state overlap fidelity
1024+ def quantum_kernel(x1, x2):
1025+ """Compute the quantum kernel (fidelity) between two vectors x1, x2."""
1026+ # circuit returns probability array; index 0 corresponds to state |000>
1027+ return kernel_circuit(x1, x2)[0]
1028+
1029+ # Test the kernel on two training samples
1030+ k_val = quantum_kernel(X_train[0], X_train[1])
1031+ print("Kernel between first two training points:", k_val)
1032+ # Should be 1.0 when comparing a vector with itself
1033+ print("Kernel of a point with itself:", quantum_kernel(X_train[0], X_train[0]))
1034+ !ec
1035+
1036+ Explanation: We use a three-qubit device and PennyLane’s AngleEmbedding
1037+ (rotation=‘Y’) to encode each three-element vector. The circuit first
1038+ embeds $x_1$, then applies the adjoint (inverse) of the same embedding
1039+ for $x_2$. Measuring the probability of the state $\vert 000\rangle $ yields the
1040+ fidelity (overlap) between the two quantum states . We wrap this in a
1041+ Python function quantum$\_$kernel$(x_1, x_2)$ for convenience.
1042+
1043+
1044+
1045+ !split
1046+ ===== Kernel matrix computation =====
1047+
1048+
1049+
1050+
1051+
1052+ We build the Gram (kernel) matrix for the training data (size
1053+ N$\_$train x N$\_$train) and between test/train (N$\_$test x N$\_$train). PennyLane
1054+ provides qml.kernels.kernel$\_$matrix, but here we compute it directly
1055+ for clarity. Each matrix entry $K_{ij} = K(x_i, x_j)$ is the fidelity
1056+ between training points $x_i$ and $x_j$.
1057+
1058+ !bc pycod
1059+ # Compute kernel matrices using our quantum_kernel function
1060+ K_train = np.array([[quantum_kernel(x_i, x_j) for x_j in X_train] for x_i in X_train])
1061+ K_test = np.array([[quantum_kernel(x_i, x_j) for x_j in X_train] for x_i in X_test])
1062+
1063+ print("Shape of training Gram matrix:", K_train.shape)
1064+ !ec
1065+
1066+ For a set of input vectors, we compute the pairwise
1067+ kernel values. This produces a symmetric kernel matrix for training
1068+ data, and a (test x train) kernel matrix for prediction. In practice,
1069+ one could use qml.kernels.kernel$\_$matrix(X$\_$train, X$\_$train,
1070+ quantum$\_$kernel) as in the PennyLane docs , but the manual loop
1071+ illustrates the concept. Each entry is a number in $[0,1]$, with $1.0$ on
1072+ the diagonal (fidelity of a state with itself).
1073+
1074+
1075+
1076+
1077+
1078+ !split
1079+ ===== Training the classical SVM =====
1080+
1081+
1082+
1083+
1084+
1085+ With the quantum kernel computed, we train a classical SVM using
1086+ scikit-learn, specifying kernel='precomputed'. This tells the SVM to
1087+ use our Gram matrix directly. We fit on the training kernel matrix
1088+ K$\_$train and labels, then predict on the test kernel matrix
1089+ K$\_$test.
1090+
1091+ !bc pycod
1092+ from sklearn.svm import SVC
1093+ from sklearn.metrics import accuracy_score, precision_score, recall_score
1094+
1095+ # Train SVM with precomputed kernel
1096+ svm = SVC(kernel='precomputed')
1097+ svm.fit(K_train, y_train)
1098+
1099+ # Predict on test set using the test-train kernel matrix
1100+ y_pred = svm.predict(K_test)
1101+
1102+ # Evaluate performance
1103+ acc = accuracy_score(y_test, y_pred)
1104+ prec = precision_score(y_test, y_pred)
1105+ rec = recall_score(y_test, y_pred)
1106+
1107+ print(f"Accuracy: {acc:.2f}, Precision: {prec:.2f}, Recall: {rec:.2f}")
1108+ !ec
1109+
1110+ Explanation: We instantiate an SVM with a precomputed kernel (since
1111+ K$\_$train is the Gram matrix). After fitting on the
1112+ quantum-kernel-trained data, we predict on the test set by passing the
1113+ test/train kernel matrix K$\_$test. We then compute accuracy, precision,
1114+ and recall using standard sklearn metrics. In our synthetic example
1115+ this yields very high performance (often perfect) because the data
1116+ clusters were well-separated.
1117+
1118+
1119+
1120+
1121+
1122+ !split
1123+ ===== Results and Evaluation =====
1124+
1125+
1126+ Finally, we report the evaluation metrics and visualize the kernel
1127+ matrix and classification. For brevity, only key metrics are printed
1128+ here. In a complete analysis, one could plot the kernel matrix as a
1129+ heatmap and a confusion matrix for the classifier. Our code achieved
1130+ perfect separation (accuracy, precision, recall all approximately equal to 1.00) on this
1131+ synthetic dataset. Real-world data would typically show lower
1132+ scores. We highlight that quantum kernels have been proposed to
1133+ capture complex feature relationships that help SVMs separate
1134+ nonlinearly separable data .
1135+
1136+ !bc pycod
1137+ # Example output (metrics)
1138+ print("Model performance on test set:")
1139+ print(f" Accuracy: {acc:.3f}")
1140+ print(f" Precision: {prec:.3f}")
1141+ print(f" Recall: {rec:.3f}")
1142+ !ec
1143+
1144+ The printed metrics confirm the model’s performance. In
1145+ our toy example, the quantum kernel produced an easily separable data
1146+ representation, leading to perfect classification. In practice, one
1147+ would inspect the kernel matrix (e.g. with a heatmap) and perhaps
1148+ compare against classical kernels. Recent studies have indeed found
1149+ quantum-enhanced models promising for financial tasks like credit
1150+ scoring.
1151+
1152+
1153+ !split
1154+ ===== QSVM summary =====
1155+
1156+ This QSVM implementation shows how classical data can be mapped to
1157+ quantum states via angle encoding, how to compute the quantum kernel
1158+ via state overlap (fidelity), and how to plug that kernel into a
1159+ classical SVM.
1160+
1161+
9131162!split
9141163===== Quantum Neural Networks and Variational Circuits =====
9151164
@@ -924,12 +1173,7 @@ For example, Abbas et al. showed that certain QNNs can exhibit higher
9241173effective dimension (and thus capacity to generalize) than comparable
9251174classical networks , suggesting a potential quantum advantage.
9261175
927-
928- !split
929- ===== Structure of lecture =====
930-
931- This lecture introduces the theory and practice of QNNs and VQCs at an
932- intermediate level. We will develop the mathematical foundations
1176+ Below we develop the mathematical foundations
9331177(state preparation, parameterized unitaries, measurement), discuss
9341178optimization and training challenges, and work through practical code
9351179examples using PennyLane.
0 commit comments