Spaces:
Sleeping
Sleeping
| """ | |
| 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() | |