Skip to content

Commit 1d6256a

Browse files
committed
update week 15
1 parent f45d3d4 commit 1d6256a

File tree

10 files changed

+2215
-204
lines changed

10 files changed

+2215
-204
lines changed

doc/pub/week15/html/week15-bs.html

Lines changed: 364 additions & 7 deletions
Large diffs are not rendered by default.

doc/pub/week15/html/week15-reveal.html

Lines changed: 345 additions & 6 deletions
Large diffs are not rendered by default.

doc/pub/week15/html/week15-solarized.html

Lines changed: 357 additions & 6 deletions
Large diffs are not rendered by default.

doc/pub/week15/html/week15.html

Lines changed: 357 additions & 6 deletions
Large diffs are not rendered by default.
0 Bytes
Binary file not shown.

doc/pub/week15/ipynb/week15.ipynb

Lines changed: 518 additions & 165 deletions
Large diffs are not rendered by default.

doc/pub/week15/pdf

-494 KB
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
\mode<presentation>
2+
\usecolortheme[rgb={0.8, 0.2, 0}]{structure}
3+
\usefonttheme[onlysmall]{structurebold}
4+
5+
\setbeamertemplate{navigation symbols}{}
6+
%\setbeamertemplate{footline}[frame number]
7+
8+
\usepackage{tikz}
9+
\usetikzlibrary{arrows,shapes,backgrounds,decorations,mindmap}
10+
11+
\mode
12+
<all>

doc/src/week15/make.sh

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,20 @@ system doconce format html $name --html_style=bootstrap --pygments_html_style=de
4949
# IPython notebook
5050
system doconce format ipynb $name $opt
5151

52-
# Ordinary plain LaTeX document
53-
system doconce format pdflatex $name --minted_latex_style=trac --latex_admon=paragraph $opt
52+
53+
# LaTeX Beamer slides
54+
beamertheme=red_plain
55+
system doconce format pdflatex $name --latex_title_layout=beamer --latex_table_format=footnotesize $opt
5456
system doconce ptex2tex $name envir=minted
5557
# Add special packages
5658
doconce subst "% Add user's preamble" "\g<1>\n\\usepackage{simplewick}" $name.tex
57-
doconce replace 'section{' 'section*{' $name.tex
58-
pdflatex -shell-escape $name
59-
pdflatex -shell-escape $name
60-
mv -f $name.pdf ${name}.pdf
59+
system doconce slides_beamer $name --beamer_slide_theme=$beamertheme
60+
system pdflatex -shell-escape ${name}
61+
system pdflatex -shell-escape ${name}
62+
cp $name.pdf ${name}.pdf
6163
cp $name.tex ${name}.tex
6264

6365

64-
65-
6666
# Publish
6767
dest=../../pub
6868
if [ ! -d $dest/$name ]; then
@@ -96,3 +96,7 @@ cp ${ipynb_tarfile} $dest/$name/ipynb
9696

9797

9898

99+
100+
101+
102+

doc/src/week15/week15.do.txt

Lines changed: 250 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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
9241173
effective dimension (and thus capacity to generalize) than comparable
9251174
classical 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
9341178
optimization and training challenges, and work through practical code
9351179
examples using PennyLane.

0 commit comments

Comments
 (0)