Hasarindu Perera commited on
Commit
d6a3aaa
·
unverified ·
1 Parent(s): df233ec

chore: update demo for v0.10.0 — new encoders, Audit tab, barren plateau badge, rff fix

Browse files
Files changed (2) hide show
  1. app.py +120 -57
  2. requirements.txt +1 -1
app.py CHANGED
@@ -1,4 +1,4 @@
1
- """QuPrep — HuggingFace Spaces demo (v0.8.0)"""
2
 
3
  from __future__ import annotations
4
 
@@ -89,14 +89,15 @@ SAMPLES = {
89
  # ---------------------------------------------------------------------------
90
 
91
  ENCODINGS = [
92
- "angle", "entangled_angle", "amplitude", "basis", "iqp",
93
  "reupload", "hamiltonian", "zz_feature_map", "pauli_feature_map",
94
- "random_fourier", "tensor_product", "qaoa_problem",
95
  ]
96
 
97
  ENCODING_DESC = {
98
  "angle": "Ry/Rx/Rz rotation per feature. NISQ-safe, depth O(1).",
99
  "entangled_angle": "Rotation + CNOT entangling layers. NISQ-safe.",
 
100
  "amplitude": "Feature vector as quantum state amplitudes. Qubit-efficient.",
101
  "basis": "Binary features encoded as X gates. NISQ-safe.",
102
  "iqp": "Havlíček ZZ feature map. Good for kernel methods.",
@@ -107,8 +108,28 @@ ENCODING_DESC = {
107
  "random_fourier": "RBF kernel approximation via random Fourier features.",
108
  "tensor_product": "Ry+Rz per qubit — full Bloch sphere, qubit-efficient.",
109
  "qaoa_problem": "QAOA-inspired feature map. Features as cost Hamiltonian parameters.",
 
110
  }
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  FRAMEWORKS = ["qasm", "qiskit", "pennylane", "cirq", "tket", "braket", "qsharp", "iqm"]
113
 
114
  TASKS = ["classification", "regression", "kernel", "qaoa", "simulation"]
@@ -197,39 +218,9 @@ def _get_exporter(framework: str):
197
  return _map.get(framework, _map["qasm"])()
198
 
199
  def _prepare_rff(X_full, X_slice, framework):
200
- """Manually fit RandomFourierEncoder on full data and encode/export the slice."""
201
- from quprep.encode.random_fourier import RandomFourierEncoder
202
- from quprep.core.dataset import Dataset
203
- enc = RandomFourierEncoder()
204
- enc.fit(X_full)
205
- ds = Dataset(data=X_slice,
206
- feature_names=[f"f{i}" for i in range(X_slice.shape[1])],
207
- feature_types=["continuous"] * X_slice.shape[1],
208
- metadata={})
209
- encoded_list = enc.encode_batch(ds)
210
- exporter = _get_exporter(framework)
211
- circuits = [exporter.export(e) for e in encoded_list]
212
-
213
- from quprep.validation.cost import CostEstimate
214
- n_qubits = enc.n_components
215
- cost = CostEstimate(
216
- encoding="random_fourier",
217
- n_features=X_slice.shape[1],
218
- n_qubits=n_qubits,
219
- gate_count=n_qubits,
220
- circuit_depth=1,
221
- two_qubit_gates=0,
222
- nisq_safe=True,
223
- warning=None,
224
- )
225
-
226
- class _FakeResult:
227
- pass
228
- r = _FakeResult()
229
- r.circuits = circuits
230
- r.encoded = encoded_list
231
- r.cost = cost
232
- return r
233
 
234
  def _reducer_kwargs(X_slice, X_full, n_qubits):
235
  """Return (kwargs_dict, clamp_note) — applies PCA reducer if budget < n_features.
@@ -247,7 +238,7 @@ def _encode_tabular(X, encoding, framework, n_samples, n_qubits):
247
  import quprep as qd
248
  X_slice = X[:max(1, n_samples)]
249
  if encoding == "random_fourier":
250
- return _prepare_rff(X, X_slice, framework), None
251
  kw, note = _reducer_kwargs(X_slice, X, n_qubits)
252
  return qd.prepare(X_slice, encoding=encoding, framework=framework, **kw), note
253
 
@@ -255,7 +246,7 @@ def _encode_image(X, encoding, framework, n_samples, n_qubits=0):
255
  import quprep as qd
256
  X_slice = X[:max(1, n_samples)]
257
  if encoding == "random_fourier":
258
- return _prepare_rff(X, X_slice, framework), None
259
  kw, note = _reducer_kwargs(X_slice, X, n_qubits)
260
  return qd.prepare(X_slice, encoding=encoding, framework=framework, **kw), note
261
 
@@ -269,7 +260,7 @@ def _encode_timeseries(X, encoding, framework, n_samples, n_qubits=0, window=4):
269
  X_win = ds_win.data
270
  X_slice = X_win[:max(1, n_samples)]
271
  if encoding == "random_fourier":
272
- return _prepare_rff(X_win, X_slice, framework), None
273
  kw, note = _reducer_kwargs(X_slice, X_win, n_qubits)
274
  return qd.prepare(X_slice, encoding=encoding, framework=framework, **kw), note
275
 
@@ -279,7 +270,7 @@ def _encode_text(sentences, encoding, framework, n_qubits=0):
279
  dataset = TextIngester(method="tfidf", max_features=8).load(sentences)
280
  X = dataset.data
281
  if encoding == "random_fourier":
282
- return _prepare_rff(X, X, framework), None
283
  kw, note = _reducer_kwargs(X, X, n_qubits)
284
  return qd.prepare(X, encoding=encoding, framework=framework, **kw), note
285
 
@@ -404,6 +395,26 @@ def run_convert(source, csv_file, sample_name, hf_name, hf_split,
404
  </div>{warn}
405
  </div>"""
406
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  n_circ = len(circuits)
408
  shape = f"{X_num.shape[0]}×{X_num.shape[1]}" if X_num.ndim == 2 else str(len(X))
409
  status = f"✓ {dtype} · {shape} → {n_circ} circuit(s) | showing sample 0"
@@ -595,6 +606,54 @@ def run_suggest(source, csv_file, sample_name, hf_name, hf_split, task, max_qubi
595
  # Fingerprint
596
  # ---------------------------------------------------------------------------
597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598
  def run_fingerprint(source, csv_file, sample_name, hf_name, hf_split,
599
  encoding, framework, reducer_type, n_components, n_qubits,
600
  use_scaler, scaler_strategy,
@@ -607,21 +666,7 @@ def run_fingerprint(source, csv_file, sample_name, hf_name, hf_split,
607
  if dtype not in ("tabular", "image", "timeseries"):
608
  return "", "<p style='color:#fbbf24'>⚠️ Fingerprinting works on numeric data (tabular / image / time series).</p>"
609
  X_use = X
610
- _enc_map = {
611
- "angle": qd.AngleEncoder,
612
- "amplitude": qd.AmplitudeEncoder,
613
- "basis": qd.BasisEncoder,
614
- "iqp": qd.IQPEncoder,
615
- "entangled_angle": qd.EntangledAngleEncoder,
616
- "reupload": qd.ReUploadEncoder,
617
- "hamiltonian": qd.HamiltonianEncoder,
618
- "zz_feature_map": qd.ZZFeatureMapEncoder,
619
- "pauli_feature_map": qd.PauliFeatureMapEncoder,
620
- "random_fourier": qd.RandomFourierEncoder,
621
- "tensor_product": qd.TensorProductEncoder,
622
- "qaoa_problem": qd.QAOAProblemEncoder,
623
- }
624
- encoder_cls = _enc_map.get(encoding, qd.AngleEncoder)
625
  encoder = encoder_cls()
626
  if hasattr(encoder, "fit"):
627
  encoder.fit(X_use)
@@ -743,7 +788,7 @@ with gr.Blocks(title="QuPrep — Quantum Data Preparation") as demo:
743
  with gr.Column(scale=1, elem_id="header-left"):
744
  gr.HTML("""
745
  <p style="margin:0 0 2px;font-size:1.5rem;font-weight:700;color:#e2e8f0">⚛️ QuPrep</p>
746
- <p style="margin:0 0 14px;font-size:0.9rem;font-weight:500;color:#a78bfa">v0.8.0 · Quantum Data Preparation</p>
747
  <p style="margin:0 0 14px;font-size:0.85rem;color:#94a3b8;line-height:1.6">
748
  The missing preprocessing layer between classical datasets and quantum computing.
749
  </p>
@@ -898,6 +943,22 @@ with gr.Blocks(title="QuPrep — Quantum Data Preparation") as demo:
898
 
899
  sug_btn.click(fn=_sug, inputs=_inputs(sug_task, sug_max), outputs=sug_out)
900
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
901
  # Fingerprint ──────────────────────────────────────────────────────
902
  with gr.TabItem("Fingerprint"):
903
  with gr.Row():
@@ -974,12 +1035,14 @@ Connect → Ingest → Clean → Reduce → Normalise → Encode → Export
974
  Supports tabular, image, time series, graph, and text data.
975
  Data connectors: HuggingFace Hub, OpenML, Kaggle, CSV/NumPy upload.
976
 
977
- ### Supported encodings (13)
978
  | Encoding | Qubits | NISQ-safe |
979
  |---|---|---|
980
  | Angle | d | ✓ |
 
981
  | Amplitude | ⌈log₂d⌉ | ✗ |
982
  | Basis | d | ✓ |
 
983
  | IQP | d | conditional |
984
  | Entangled Angle | d | ✓ |
985
  | Re-Upload | d | ✓ |
 
1
+ """QuPrep — HuggingFace Spaces demo (v0.10.0)"""
2
 
3
  from __future__ import annotations
4
 
 
89
  # ---------------------------------------------------------------------------
90
 
91
  ENCODINGS = [
92
+ "angle", "entangled_angle", "dense_angle", "amplitude", "basis", "iqp",
93
  "reupload", "hamiltonian", "zz_feature_map", "pauli_feature_map",
94
+ "random_fourier", "tensor_product", "qaoa_problem", "discretized",
95
  ]
96
 
97
  ENCODING_DESC = {
98
  "angle": "Ry/Rx/Rz rotation per feature. NISQ-safe, depth O(1).",
99
  "entangled_angle": "Rotation + CNOT entangling layers. NISQ-safe.",
100
+ "dense_angle": "Ry+Rz per qubit — 2 features per qubit, halves qubit count vs angle. New in v0.10.",
101
  "amplitude": "Feature vector as quantum state amplitudes. Qubit-efficient.",
102
  "basis": "Binary features encoded as X gates. NISQ-safe.",
103
  "iqp": "Havlíček ZZ feature map. Good for kernel methods.",
 
108
  "random_fourier": "RBF kernel approximation via random Fourier features.",
109
  "tensor_product": "Ry+Rz per qubit — full Bloch sphere, qubit-efficient.",
110
  "qaoa_problem": "QAOA-inspired feature map. Features as cost Hamiltonian parameters.",
111
+ "discretized": "Continuous → binary string with configurable precision. QUBO-ready. New in v0.10.",
112
  }
113
 
114
+ # Shared encoder name → class map (used by Convert, Audit, Fingerprint)
115
+ def _get_enc_map(qd):
116
+ return {
117
+ "angle": qd.AngleEncoder,
118
+ "entangled_angle": qd.EntangledAngleEncoder,
119
+ "dense_angle": qd.DenseAngleEncoder,
120
+ "amplitude": qd.AmplitudeEncoder,
121
+ "basis": qd.BasisEncoder,
122
+ "iqp": qd.IQPEncoder,
123
+ "reupload": qd.ReUploadEncoder,
124
+ "hamiltonian": qd.HamiltonianEncoder,
125
+ "zz_feature_map": qd.ZZFeatureMapEncoder,
126
+ "pauli_feature_map": qd.PauliFeatureMapEncoder,
127
+ "random_fourier": qd.RandomFourierEncoder,
128
+ "tensor_product": qd.TensorProductEncoder,
129
+ "qaoa_problem": qd.QAOAProblemEncoder,
130
+ "discretized": qd.DiscretizedEncoder,
131
+ }
132
+
133
  FRAMEWORKS = ["qasm", "qiskit", "pennylane", "cirq", "tket", "braket", "qsharp", "iqm"]
134
 
135
  TASKS = ["classification", "regression", "kernel", "qaoa", "simulation"]
 
218
  return _map.get(framework, _map["qasm"])()
219
 
220
  def _prepare_rff(X_full, X_slice, framework):
221
+ """v0.10.0: RandomFourierEncoder is now auto-fit by Pipeline — plain prepare() works."""
222
+ import quprep as qd
223
+ return qd.prepare(X_slice, encoding="random_fourier", framework=framework), None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
  def _reducer_kwargs(X_slice, X_full, n_qubits):
226
  """Return (kwargs_dict, clamp_note) — applies PCA reducer if budget < n_features.
 
238
  import quprep as qd
239
  X_slice = X[:max(1, n_samples)]
240
  if encoding == "random_fourier":
241
+ return _prepare_rff(X, X_slice, framework)
242
  kw, note = _reducer_kwargs(X_slice, X, n_qubits)
243
  return qd.prepare(X_slice, encoding=encoding, framework=framework, **kw), note
244
 
 
246
  import quprep as qd
247
  X_slice = X[:max(1, n_samples)]
248
  if encoding == "random_fourier":
249
+ return _prepare_rff(X, X_slice, framework)
250
  kw, note = _reducer_kwargs(X_slice, X, n_qubits)
251
  return qd.prepare(X_slice, encoding=encoding, framework=framework, **kw), note
252
 
 
260
  X_win = ds_win.data
261
  X_slice = X_win[:max(1, n_samples)]
262
  if encoding == "random_fourier":
263
+ return _prepare_rff(X_win, X_slice, framework)
264
  kw, note = _reducer_kwargs(X_slice, X_win, n_qubits)
265
  return qd.prepare(X_slice, encoding=encoding, framework=framework, **kw), note
266
 
 
270
  dataset = TextIngester(method="tfidf", max_features=8).load(sentences)
271
  X = dataset.data
272
  if encoding == "random_fourier":
273
+ return _prepare_rff(X, X, framework)
274
  kw, note = _reducer_kwargs(X, X, n_qubits)
275
  return qd.prepare(X, encoding=encoding, framework=framework, **kw), note
276
 
 
395
  </div>{warn}
396
  </div>"""
397
 
398
+ # Barren plateau badge (tabular/image/timeseries only, silent on error)
399
+ if X_num.ndim == 2 and X_num.size > 0 and dtype not in ("graph", "text"):
400
+ try:
401
+ from quprep.core.dataset import Dataset as _DS
402
+ _bp = qd.detect_barren_plateau(
403
+ _get_enc_map(qd).get(encoding, qd.AngleEncoder)(),
404
+ _DS(data=X_num[:5]),
405
+ cost_type="local",
406
+ )
407
+ _col = {"none": "#4ade80", "mild": "#fbbf24",
408
+ "high": "#fb923c", "severe": "#f87171"}.get(_bp.risk_level, "#94a3b8")
409
+ cost_html += f"""
410
+ <div style="margin-top:10px;padding:8px 14px;background:#1e293b;border-radius:8px">
411
+ <span style="color:#94a3b8;font-size:0.78rem">Barren plateau</span>
412
+ <span style="float:right;color:{_col};font-weight:700;font-size:0.85rem">{_bp.risk_level.upper()}</span>
413
+ <div style="clear:both;color:#64748b;font-size:0.72rem;margin-top:2px">depth {_bp.circuit_depth} · {_bp.n_qubits} qubits</div>
414
+ </div>"""
415
+ except Exception:
416
+ pass
417
+
418
  n_circ = len(circuits)
419
  shape = f"{X_num.shape[0]}×{X_num.shape[1]}" if X_num.ndim == 2 else str(len(X))
420
  status = f"✓ {dtype} · {shape} → {n_circ} circuit(s) | showing sample 0"
 
606
  # Fingerprint
607
  # ---------------------------------------------------------------------------
608
 
609
+ def run_audit(source, csv_file, sample_name, hf_name, hf_split, encoding, qubit_budget):
610
+ """preprocessing_report() — structured dataset audit before encoding."""
611
+ try:
612
+ import quprep as qd
613
+ dtype, X, cols = load_data(source, csv_file, sample_name, hf_name, hf_split)
614
+ except Exception as exc:
615
+ return f"<p style='color:#f87171'>❌ {exc}</p>"
616
+
617
+ if dtype != "tabular":
618
+ return "<p style='color:#fbbf24'>⚠️ Audit works on tabular data only.</p>"
619
+
620
+ try:
621
+ ds = qd.NumpyIngester().load(X)
622
+ enc = _get_enc_map(qd).get(encoding, qd.AngleEncoder)()
623
+ budget = int(qubit_budget) if qubit_budget > 0 else X.shape[1]
624
+ report = qd.preprocessing_report(ds, encoder=enc, qubit_budget=budget)
625
+ except Exception as exc:
626
+ return f"<p style='color:#f87171'>❌ {exc}</p>"
627
+
628
+ sev_color = "#4ade80" if report.n_issues == 0 else ("#fbbf24" if report.n_issues <= 2 else "#f87171")
629
+ recs_html = "".join(
630
+ f"<li style='margin:4px 0;color:#cbd5e1;font-size:0.85rem'>{r}</li>"
631
+ for r in report.recommendations
632
+ ) if report.recommendations else "<li style='color:#4ade80'>No issues — data looks healthy.</li>"
633
+
634
+ return f"""
635
+ <div style="font-family:sans-serif;font-size:0.9rem;line-height:1.6">
636
+ <div style="display:flex;align-items:center;gap:12px;margin-bottom:16px">
637
+ <span style="font-size:2rem;font-weight:700;color:{sev_color}">{report.n_issues}</span>
638
+ <span style="color:#94a3b8">issue(s) found · encoding={encoding} · qubit budget={budget}</span>
639
+ </div>
640
+ <div style="background:#1e293b;border-radius:8px;padding:14px 18px">
641
+ <div style="color:#64748b;font-size:0.78rem;font-weight:600;margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em">Recommendations</div>
642
+ <ul style="margin:0;padding-left:18px">{recs_html}</ul>
643
+ </div>
644
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:12px">
645
+ <div style="background:#1e293b;border-radius:8px;padding:10px 14px">
646
+ <div style="color:#64748b;font-size:0.78rem">Shape</div>
647
+ <div style="font-weight:600">{X.shape[0]} × {X.shape[1]}</div>
648
+ </div>
649
+ <div style="background:#1e293b;border-radius:8px;padding:10px 14px">
650
+ <div style="color:#64748b;font-size:0.78rem">Encoder</div>
651
+ <div style="font-weight:600">{encoding}</div>
652
+ </div>
653
+ </div>
654
+ </div>"""
655
+
656
+
657
  def run_fingerprint(source, csv_file, sample_name, hf_name, hf_split,
658
  encoding, framework, reducer_type, n_components, n_qubits,
659
  use_scaler, scaler_strategy,
 
666
  if dtype not in ("tabular", "image", "timeseries"):
667
  return "", "<p style='color:#fbbf24'>⚠️ Fingerprinting works on numeric data (tabular / image / time series).</p>"
668
  X_use = X
669
+ encoder_cls = _get_enc_map(qd).get(encoding, qd.AngleEncoder)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
670
  encoder = encoder_cls()
671
  if hasattr(encoder, "fit"):
672
  encoder.fit(X_use)
 
788
  with gr.Column(scale=1, elem_id="header-left"):
789
  gr.HTML("""
790
  <p style="margin:0 0 2px;font-size:1.5rem;font-weight:700;color:#e2e8f0">⚛️ QuPrep</p>
791
+ <p style="margin:0 0 14px;font-size:0.9rem;font-weight:500;color:#a78bfa">v0.10.0 · Quantum Data Preparation</p>
792
  <p style="margin:0 0 14px;font-size:0.85rem;color:#94a3b8;line-height:1.6">
793
  The missing preprocessing layer between classical datasets and quantum computing.
794
  </p>
 
943
 
944
  sug_btn.click(fn=_sug, inputs=_inputs(sug_task, sug_max), outputs=sug_out)
945
 
946
+ # Audit ────────────────────────────────────────────────────────────
947
+ with gr.TabItem("Audit"):
948
+ with gr.Row():
949
+ with gr.Column(scale=1):
950
+ aud_enc = gr.Dropdown(choices=ENCODINGS, value="angle", label="Target encoding")
951
+ aud_qsl = gr.Slider(0, 30, value=0, step=1, label="Qubit budget (0 = n_features)")
952
+ aud_btn = gr.Button("Audit →", variant="primary")
953
+ gr.HTML('<p style="font-size:0.82rem;color:#475569;margin:8px 0">Checks NaN, outliers, qubit budget, class imbalance, and encoder compatibility before you encode. Tabular data only.</p>')
954
+ with gr.Column(scale=3):
955
+ aud_out = gr.HTML(value="<p style='color:#475569;padding:24px 0'>Click <strong>Audit →</strong></p>")
956
+
957
+ def _aud(src, csv, samp, hfn, hfs, enc, nq):
958
+ return run_audit(_src(src), csv, samp, hfn, hfs, enc, nq)
959
+
960
+ aud_btn.click(fn=_aud, inputs=_inputs(aud_enc, aud_qsl), outputs=aud_out)
961
+
962
  # Fingerprint ──────────────────────────────────────────────────────
963
  with gr.TabItem("Fingerprint"):
964
  with gr.Row():
 
1035
  Supports tabular, image, time series, graph, and text data.
1036
  Data connectors: HuggingFace Hub, OpenML, Kaggle, CSV/NumPy upload.
1037
 
1038
+ ### Supported encodings (15)
1039
  | Encoding | Qubits | NISQ-safe |
1040
  |---|---|---|
1041
  | Angle | d | ✓ |
1042
+ | Dense Angle *(v0.10)* | ⌈d/2⌉ | ✓ |
1043
  | Amplitude | ⌈log₂d⌉ | ✗ |
1044
  | Basis | d | ✓ |
1045
+ | Discretized *(v0.10)* | d×bits | ✓ |
1046
  | IQP | d | conditional |
1047
  | Entangled Angle | d | ✓ |
1048
  | Re-Upload | d | ✓ |
requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
- quprep[qiskit,pennylane,cirq,tket,braket,qsharp,viz,huggingface,openml,image,text]>=0.8.0
2
  gradio>=4.0
3
  pandas
4
  numpy
 
1
+ quprep[qiskit,pennylane,cirq,tket,braket,qsharp,viz,huggingface,openml,image,text]==0.10.0
2
  gradio>=4.0
3
  pandas
4
  numpy