Vijayarv07's picture
Update app.py
d1f671b verified
Raw
History Blame Contribute Delete
6.96 kB
"""
HuggingFace Space — Quantum Iris Classifier
Interactive demo: classify Iris flowers using a Variational Quantum Circuit
Built by Vijaya Kumari | github.com/vijayarjun7
"""
import gradio as gr
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import io, warnings
warnings.filterwarnings("ignore")
from PIL import Image
from sklearn.datasets import load_iris
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from qiskit.circuit.library import ZZFeatureMap, RealAmplitudes
from qiskit_machine_learning.algorithms import VQC
from qiskit_algorithms.optimizers import COBYLA
from qiskit.primitives import Sampler
# -- Train VQC at startup
print("Training quantum classifier... (runs once at startup)")
iris = load_iris()
X = iris.data[:100, 2:4]
y = iris.target[:100]
scaler = MinMaxScaler(feature_range=(0, np.pi))
X_sc = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(
X_sc, y, test_size=0.2, random_state=42, stratify=y
)
feature_map = ZZFeatureMap(feature_dimension=2, reps=2)
ansatz = RealAmplitudes(num_qubits=2, reps=3)
vqc = VQC(
sampler=Sampler(),
feature_map=feature_map,
ansatz=ansatz,
optimizer=COBYLA(maxiter=150, rhobeg=0.5),
)
vqc.fit(X_train, y_train)
test_acc = vqc.score(X_test, y_test)
print(f"VQC trained! Test accuracy: {test_acc:.1%}")
# -- Pre-compute decision boundary once at startup
print("Pre-computing decision boundary...")
h = 0.08
x0_min, x0_max = X_sc[:, 0].min() - 0.1, X_sc[:, 0].max() + 0.1
x1_min, x1_max = X_sc[:, 1].min() - 0.1, X_sc[:, 1].max() + 0.1
xx, yy = np.meshgrid(np.arange(x0_min, x0_max, h),
np.arange(x1_min, x1_max, h))
Z = vqc.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
print("Decision boundary ready!")
petal_len_range = (float(X[:, 0].min()), float(X[:, 0].max()))
petal_wid_range = (float(X[:, 1].min()), float(X[:, 1].max()))
def fig_to_pil(fig):
buf = io.BytesIO()
fig.savefig(buf, format="png", dpi=120, bbox_inches="tight")
buf.seek(0)
img = Image.open(buf).copy()
plt.close(fig)
buf.close()
return img
def classify(petal_length, petal_width):
x_raw = np.array([[petal_length, petal_width]])
x_sc = scaler.transform(x_raw)
pred = vqc.predict(x_sc)[0]
label = "Setosa" if pred == 0 else "Versicolor"
confidence = "High confidence" if (x_sc[0, 0] < 1.0 or x_sc[0, 0] > 2.5) else "Moderate confidence"
fig1, ax1 = plt.subplots(figsize=(9, 2.2))
ax1.axis("off")
ax1.text(0.5, 0.65,
f"Input: petal_length={petal_length:.1f}cm, petal_width={petal_width:.1f}cm",
ha="center", va="center", fontsize=13, fontweight="bold", color="#222")
ax1.text(0.5, 0.28,
f"Quantum angles: [{x_sc[0,0]:.3f} rad, {x_sc[0,1]:.3f} rad] -> Prediction: {label}",
ha="center", va="center", fontsize=11,
bbox=dict(boxstyle="round,pad=0.5", facecolor="#ede0ff", alpha=0.95))
ax1.set_title(
"ZZFeatureMap (data encoding) + RealAmplitudes (8 trainable weights) + COBYLA",
fontsize=9, color="#555", pad=5)
img1 = fig_to_pil(fig1)
fig2, ax2 = plt.subplots(figsize=(6, 5))
ax2.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.RdBu)
ax2.contour(xx, yy, Z, colors="gray", linewidths=0.8, alpha=0.5)
for cls, (col, lbl) in enumerate(zip(
["#E74C3C", "#2980B9"],
["Setosa (train)", "Versicolor (train)"]
)):
mask = y_train == cls
ax2.scatter(X_train[mask, 0], X_train[mask, 1],
c=col, s=40, label=lbl, alpha=0.7,
edgecolors="white", lw=0.4)
star_col = "#E74C3C" if pred == 0 else "#2980B9"
ax2.scatter(x_sc[0, 0], x_sc[0, 1], c=star_col, s=350,
marker="*", edgecolors="gold", linewidths=1.8,
zorder=10, label=f"Your input -> {label}")
ax2.set_xlabel("Petal Length (radians)")
ax2.set_ylabel("Petal Width (radians)")
ax2.set_title(f"VQC Decision Boundary | Test Accuracy: {test_acc:.1%}",
fontsize=11, fontweight="bold")
ax2.legend(fontsize=9, loc="upper left")
ax2.grid(True, alpha=0.2)
img2 = fig_to_pil(fig2)
result_text = f"""
**Prediction: {label}** - {confidence}
**How it decided:**
1. Inputs scaled to quantum angles: [{x_sc[0,0]:.3f}, {x_sc[0,1]:.3f}] radians
2. ZZFeatureMap encoded into 2-qubit state via H + ZZ-entanglement
3. RealAmplitudes applied 8 trained rotation angles
4. Qubit measured: {'|0> = Setosa' if pred == 0 else '|1> = Versicolor'}
**Model:** 2-qubit VQC | 8 parameters | {test_acc:.1%} test accuracy
"""
return result_text, img1, img2
with gr.Blocks(
title="Quantum Iris Classifier",
theme=gr.themes.Soft(primary_hue="purple"),
css=".gradio-container { max-width: 900px; margin: auto; }"
) as demo:
gr.Markdown("""
# Quantum Iris Classifier
### Variational Quantum Circuit (VQC) - Qiskit Machine Learning
A 2-qubit quantum circuit classifies Iris flowers by encoding petal measurements
as quantum rotation angles, then using trained quantum weights to predict the species.
> **Setosa** = short narrow petals | **Versicolor** = long wide petals
""")
with gr.Row():
with gr.Column(scale=1):
petal_len = gr.Slider(
minimum=round(petal_len_range[0], 1),
maximum=round(petal_len_range[1], 1),
value=1.5, step=0.1,
label="Petal Length (cm)",
info="Setosa: 1.0-1.9cm | Versicolor: 3.0-5.1cm"
)
petal_wid = gr.Slider(
minimum=round(petal_wid_range[0], 1),
maximum=round(petal_wid_range[1], 1),
value=0.3, step=0.1,
label="Petal Width (cm)",
info="Setosa: 0.1-0.6cm | Versicolor: 1.0-1.8cm"
)
classify_btn = gr.Button("Run Quantum Classifier", variant="primary")
with gr.Column(scale=2):
result_md = gr.Markdown()
with gr.Row():
circuit_img = gr.Image(label="Quantum Circuit Info", type="pil")
boundary_img = gr.Image(label="Decision Boundary (your input = star)", type="pil")
classify_btn.click(
fn=classify,
inputs=[petal_len, petal_wid],
outputs=[result_md, circuit_img, boundary_img]
)
gr.Markdown("""
---
**How this works:**
- **ZZFeatureMap** encodes petal measurements as rotation angles in a 2-qubit quantum state
- **ZZ-entanglement** captures petal length x width interaction automatically
- **RealAmplitudes** = 8 Ry rotation angles trained with COBYLA (gradient-free)
- Measurement: |0> Setosa | |1> Versicolor
Built by [Vijaya Kumari](https://github.com/vijayarjun7) | Quantum ML Learning Journey
""")
if __name__ == "__main__":
demo.launch()