QSBench commited on
Commit
e142e52
·
verified ·
1 Parent(s): c906129

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +294 -118
app.py CHANGED
@@ -1,136 +1,312 @@
 
 
 
1
  import gradio as gr
2
- import pandas as pd
3
  import numpy as np
 
4
  from datasets import load_dataset
5
  from sklearn.ensemble import RandomForestRegressor
6
- from sklearn.metrics import r2_score
7
- import matplotlib.pyplot as plt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- # Загружаем датасет и разделяем по колонке 'split'
10
- print("Loading dataset...")
11
- ds_all = load_dataset("QSBench/QSBench-Core-v1.0.0-demo")
12
- df_all = pd.DataFrame(ds_all['train'])
13
-
14
- splits = {}
15
- for split_name in df_all['split'].unique():
16
- splits[split_name] = df_all[df_all['split'] == split_name].reset_index(drop=True)
17
-
18
- print("Available splits:", list(splits.keys()))
19
-
20
- # Список потенциальных признаков (числовые колонки, которые не являются целевыми или текстовыми)
21
- numeric_cols = df_all.select_dtypes(include=[np.number]).columns.tolist()
22
- # Исключаем явные целевые переменные и идентификаторы
23
- exclude = ['sample_id', 'sample_seed', 'ideal_expval_Z_global', 'ideal_expval_X_global', 'ideal_expval_Y_global',
24
- 'noisy_expval_Z_global', 'noisy_expval_X_global', 'noisy_expval_Y_global',
25
- 'error_Z_global', 'error_X_global', 'error_Y_global',
26
- 'sign_ideal_Z_global', 'sign_noisy_Z_global',
27
- 'ideal_expval_Z_q0', 'ideal_expval_Z_q1', 'ideal_expval_Z_q2', 'ideal_expval_Z_q3', 'ideal_expval_Z_q4', 'ideal_expval_Z_q5',
28
- 'noisy_expval_Z_q0', 'noisy_expval_Z_q1', 'noisy_expval_Z_q2', 'noisy_expval_Z_q3', 'noisy_expval_Z_q4', 'noisy_expval_Z_q5',
29
- 'ideal_expval_X_q0', 'ideal_expval_X_q1', 'ideal_expval_X_q2', 'ideal_expval_X_q3', 'ideal_expval_X_q4', 'ideal_expval_X_q5',
30
- 'noisy_expval_X_q0', 'noisy_expval_X_q1', 'noisy_expval_X_q2', 'noisy_expval_X_q3', 'noisy_expval_X_q4', 'noisy_expval_X_q5',
31
- 'ideal_expval_Y_q0', 'ideal_expval_Y_q1', 'ideal_expval_Y_q2', 'ideal_expval_Y_q3', 'ideal_expval_Y_q4', 'ideal_expval_Y_q5',
32
- 'noisy_expval_Y_q0', 'noisy_expval_Y_q1', 'noisy_expval_Y_q2', 'noisy_expval_Y_q3', 'noisy_expval_Y_q4', 'noisy_expval_Y_q5']
33
- feature_cols = [col for col in numeric_cols if col not in exclude and not col.startswith('error_')]
34
-
35
- # Целевая переменная
36
- target_col = "ideal_expval_Z_global"
37
-
38
- def show_data(split):
39
- if split in splits:
40
- return splits[split].head(10)
41
- else:
42
- return f"Split '{split}' not found"
43
-
44
- def train_model():
45
- if 'train' not in splits or 'test' not in splits:
46
- return None, "Error: train or test split not found in dataset"
47
-
48
- # Проверяем наличие признаков
49
- available_features = [col for col in feature_cols if col in splits['train'].columns]
50
- if not available_features:
51
- return None, f"Error: no numeric feature columns found (tried: {feature_cols})"
52
-
53
- X_train = splits['train'][available_features]
54
- y_train = splits['train'][target_col]
55
- X_test = splits['test'][available_features]
56
- y_test = splits['test'][target_col]
57
-
58
- model = RandomForestRegressor(n_estimators=100, random_state=42)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  model.fit(X_train, y_train)
60
- y_pred = model.predict(X_test)
61
- r2 = r2_score(y_test, y_pred)
 
 
62
 
63
- # График предсказаний
64
- fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
65
- ax1.scatter(y_test, y_pred, alpha=0.5)
66
- ax1.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--')
 
 
67
  ax1.set_xlabel("True value")
68
- ax1.set_ylabel("Predicted")
69
- ax1.set_title(f"Predictions vs. Truth\nR² = {r2:.4f}")
70
 
71
- # Важность признаков
72
  importances = model.feature_importances_
73
- indices = np.argsort(importances)[-10:] # топ-10 признаков
74
- ax2.barh(range(len(indices)), importances[indices])
75
- ax2.set_yticks(range(len(indices)))
76
- ax2.set_yticklabels([available_features[i] for i in indices])
77
  ax2.set_xlabel("Feature importance")
78
- ax2.set_title("Top 10 most important features")
79
 
80
  plt.tight_layout()
81
- explanation = f"""
82
- **R² score:** {r2:.4f}
83
 
84
- **What does it mean?**
85
- measures how well the model explains the variance in the target.
86
- - 1.0 = perfect prediction
87
- - 0.0 = model predicts the mean (no better than guessing)
88
- - Negative values = model performs worse than guessing the mean.
89
 
90
- The current score is negative, which indicates that the chosen features (`total_gates`, `gate_entropy`, `meyer_wallach`, and others) are not strongly predictive of the ideal Z expectation value on this small dataset.
91
- This is expected: quantum expectation values depend on many subtle circuit details. Larger datasets with richer features would allow better models.
92
 
93
- 👉 **Our full datasets** contain up to 200,000 circuits, additional noise models, and more features – perfect for serious Quantum Machine Learning research.
94
- """
95
  return fig, explanation
96
 
97
- # Интерфейс
98
- with gr.Blocks(title="QSBench Demo Explorer") as demo:
99
- gr.Markdown("""
100
- # QSBench Core Demo Explorer
101
- Interactive demo of the **QSBench Core Demo** dataset – 200 synthetic quantum circuits (6 qubits, depth 4).
102
- This space shows how to load the data, inspect it, and train a simple model on the ideal expectation values.
103
-
104
- 👉 **Full datasets (up to 200k samples, noisy versions, 10‑qubit transpilation packs) are available for purchase.**
105
- [Visit the QSBench website](https://qsbench.github.io/)
106
- """)
107
-
108
- with gr.Tabs():
109
- with gr.TabItem("Data Explorer"):
110
- split_selector = gr.Dropdown(
111
- choices=list(splits.keys()),
112
- label="Choose a split",
113
- value=list(splits.keys())[0] if splits else None
114
- )
115
- data_table = gr.Dataframe(label="First 10 rows", interactive=False)
116
- split_selector.change(fn=show_data, inputs=split_selector, outputs=data_table)
117
- demo.load(fn=lambda: show_data(list(splits.keys())[0]), outputs=data_table)
118
-
119
- with gr.TabItem("Model Demo"):
120
- train_button = gr.Button("Train Random Forest")
121
- plot_output = gr.Plot()
122
- text_output = gr.Markdown()
123
- train_button.click(fn=train_model, outputs=[plot_output, text_output])
124
-
125
- gr.Markdown("---")
126
- gr.Markdown("""
127
- ### Get the full datasets
128
- - **QSBench Core** – 75k clean circuits (8 qubits)
129
- - **Depolarizing Noise Pack** – 150k circuits with depolarizing noise
130
- - **Amplitude Damping Pack** – 150k circuits with T1‑like relaxation
131
- - **Transpilation Hardware Pack** – 200k circuits (10 qubits) with hardware‑aware transpilation
132
-
133
- 🔗 [Browse all datasets and purchase licenses](https://qsbench.github.io/)
134
- """)
135
-
136
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from pathlib import Path
3
+
4
  import gradio as gr
5
+ import matplotlib.pyplot as plt
6
  import numpy as np
7
+ import pandas as pd
8
  from datasets import load_dataset
9
  from sklearn.ensemble import RandomForestRegressor
10
+ from sklearn.metrics import mean_absolute_error, r2_score
11
+ from sklearn.model_selection import train_test_split
12
+
13
+
14
+ # =========================================================
15
+ # CONFIG
16
+ # =========================================================
17
+ HF_DATASET_NAME = "QSBench/QSBench-Core-v1.0.0-demo"
18
+ LOCAL_BENCHMARK_CSV = "noise_benchmark_results.csv"
19
+
20
+ TARGET_COL = "ideal_expval_Z_global"
21
+
22
+ EXCLUDE_COLS = {
23
+ "sample_id",
24
+ "sample_seed",
25
+ "ideal_expval_Z_global",
26
+ "ideal_expval_X_global",
27
+ "ideal_expval_Y_global",
28
+ "noisy_expval_Z_global",
29
+ "noisy_expval_X_global",
30
+ "noisy_expval_Y_global",
31
+ "error_Z_global",
32
+ "error_X_global",
33
+ "error_Y_global",
34
+ "sign_ideal_Z_global",
35
+ "sign_noisy_Z_global",
36
+ "sign_ideal_X_global",
37
+ "sign_noisy_X_global",
38
+ "sign_ideal_Y_global",
39
+ "sign_noisy_Y_global",
40
+ }
41
+
42
+ MODEL_PARAMS = dict(
43
+ n_estimators=80,
44
+ max_depth=10,
45
+ min_samples_leaf=2,
46
+ random_state=42,
47
+ n_jobs=-1,
48
+ )
49
+
50
+
51
+ # =========================================================
52
+ # DATA LOADING
53
+ # =========================================================
54
+ def load_demo_dataset() -> pd.DataFrame:
55
+ ds_all = load_dataset(HF_DATASET_NAME)
56
+ df_all = pd.DataFrame(ds_all["train"])
57
+ return df_all
58
+
59
+
60
+ def split_by_split_column(df: pd.DataFrame) -> dict:
61
+ if "split" not in df.columns:
62
+ return {"all": df.reset_index(drop=True)}
63
+
64
+ splits = {}
65
+ for split_name in df["split"].dropna().astype(str).unique():
66
+ splits[split_name] = df[df["split"].astype(str) == split_name].reset_index(drop=True)
67
+ return splits
68
+
69
 
70
+ def get_numeric_feature_cols(df: pd.DataFrame) -> list[str]:
71
+ numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist()
72
+ feature_cols = [c for c in numeric_cols if c not in EXCLUDE_COLS and not c.startswith("error_")]
73
+ return feature_cols
74
+
75
+
76
+ def load_benchmark_results() -> pd.DataFrame:
77
+ path = Path(LOCAL_BENCHMARK_CSV)
78
+ if not path.exists():
79
+ return pd.DataFrame(
80
+ [
81
+ {
82
+ "dataset": "noise_benchmark_results.csv not found",
83
+ "split_used": "",
84
+ "n_samples": 0,
85
+ "r2": np.nan,
86
+ "mae": np.nan,
87
+ "avg_noise_prob": np.nan,
88
+ "status": "missing_file",
89
+ }
90
+ ]
91
+ )
92
+
93
+ df = pd.read_csv(path)
94
+ return df
95
+
96
+
97
+ # =========================================================
98
+ # DATA EXPLORER TAB
99
+ # =========================================================
100
+ def show_data(split_name, splits_cache):
101
+ if not splits_cache:
102
+ return pd.DataFrame([{"message": "Dataset not loaded"}])
103
+
104
+ if split_name in splits_cache:
105
+ return splits_cache[split_name].head(10)
106
+
107
+ first_key = next(iter(splits_cache.keys()))
108
+ return splits_cache[first_key].head(10)
109
+
110
+
111
+ # =========================================================
112
+ # MODEL DEMO TAB
113
+ # =========================================================
114
+ def train_model_demo(df: pd.DataFrame):
115
+ if TARGET_COL not in df.columns:
116
+ return None, "Target column not found."
117
+
118
+ feature_cols = get_numeric_feature_cols(df)
119
+ if not feature_cols:
120
+ return None, "No numeric feature columns found."
121
+
122
+ work_df = df.dropna(subset=feature_cols + [TARGET_COL]).reset_index(drop=True)
123
+
124
+ X = work_df[feature_cols]
125
+ y = work_df[TARGET_COL]
126
+
127
+ if len(work_df) < 20:
128
+ return None, "Not enough rows for a stable demo."
129
+
130
+ X_train, X_test, y_train, y_test = train_test_split(
131
+ X, y, test_size=0.2, random_state=42
132
+ )
133
+
134
+ model = RandomForestRegressor(**MODEL_PARAMS)
135
  model.fit(X_train, y_train)
136
+ preds = model.predict(X_test)
137
+
138
+ r2 = r2_score(y_test, preds)
139
+ mae = mean_absolute_error(y_test, preds)
140
 
141
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13, 5))
142
+
143
+ ax1.scatter(y_test, preds, alpha=0.6)
144
+ min_v = min(float(y_test.min()), float(np.min(preds)))
145
+ max_v = max(float(y_test.max()), float(np.max(preds)))
146
+ ax1.plot([min_v, max_v], [min_v, max_v], linestyle="--")
147
  ax1.set_xlabel("True value")
148
+ ax1.set_ylabel("Predicted value")
149
+ ax1.set_title(f"Predictions vs Truth\nR² = {r2:.4f}, MAE = {mae:.4f}")
150
 
 
151
  importances = model.feature_importances_
152
+ top_idx = np.argsort(importances)[-10:]
153
+ ax2.barh(range(len(top_idx)), importances[top_idx])
154
+ ax2.set_yticks(range(len(top_idx)))
155
+ ax2.set_yticklabels([feature_cols[i] for i in top_idx])
156
  ax2.set_xlabel("Feature importance")
157
+ ax2.set_title("Top 10 features")
158
 
159
  plt.tight_layout()
 
 
160
 
161
+ explanation = f"""
162
+ **score:** {r2:.4f}
163
+ **MAE:** {mae:.4f}
 
 
164
 
165
+ This is a lightweight baseline on the demo dataset. The point is not to get a perfect score, but to show that the dataset contains real structure and can support quantum ML experiments.
166
+ """
167
 
 
 
168
  return fig, explanation
169
 
170
+
171
+ # =========================================================
172
+ # BENCHMARK TAB
173
+ # =========================================================
174
+ def make_bar_plot(df: pd.DataFrame, value_col: str, title: str, ylabel: str):
175
+ fig, ax = plt.subplots(figsize=(9, 4.8))
176
+ if df.empty or value_col not in df.columns or "dataset" not in df.columns:
177
+ ax.text(0.5, 0.5, "No benchmark data available", ha="center", va="center")
178
+ ax.axis("off")
179
+ return fig
180
+
181
+ plot_df = df.copy()
182
+ plot_df = plot_df.dropna(subset=[value_col])
183
+
184
+ ax.bar(plot_df["dataset"].astype(str), plot_df[value_col].astype(float))
185
+ ax.set_title(title)
186
+ ax.set_xlabel("Dataset")
187
+ ax.set_ylabel(ylabel)
188
+ ax.tick_params(axis="x", rotation=20)
189
+ ax.axhline(0, linewidth=1)
190
+ plt.tight_layout()
191
+ return fig
192
+
193
+
194
+ def build_benchmark_dashboard():
195
+ df = load_benchmark_results()
196
+
197
+ explanation = """
198
+ ### Noise robustness benchmark
199
+
200
+ This dashboard shows how a model trained on clean circuits behaves on:
201
+ - **core_clean**
202
+ - **depolarizing**
203
+ - **amplitude_damping**
204
+ - **transpilation**
205
+
206
+ A sharp drop in indicates strong distribution shift. That is exactly the value of the larger QSBench packs.
207
+ """
208
+
209
+ r2_fig = make_bar_plot(df, "r2", "Noise Robustness Benchmark — R²", "R²")
210
+ mae_fig = make_bar_plot(df, "mae", "Noise Robustness Benchmark — MAE", "MAE")
211
+
212
+ return df, r2_fig, mae_fig, explanation
213
+
214
+
215
+ # =========================================================
216
+ # APP
217
+ # =========================================================
218
+ def main():
219
+ print("Loading demo dataset...")
220
+ df_all = load_demo_dataset()
221
+ splits_cache = split_by_split_column(df_all)
222
+ split_choices = list(splits_cache.keys())
223
+
224
+ default_split = split_choices[0] if split_choices else None
225
+
226
+ with gr.Blocks(title="QSBench Demo Explorer") as demo:
227
+ gr.Markdown(
228
+ """
229
+ # QSBench Demo Explorer
230
+
231
+ Interactive demo for the QSBench Core demo dataset and precomputed noise robustness benchmark.
232
+ """
233
+ )
234
+
235
+ with gr.Tabs():
236
+ with gr.TabItem("Data Explorer"):
237
+ gr.Markdown("Inspect the demo dataset split by split.")
238
+ split_selector = gr.Dropdown(
239
+ choices=split_choices,
240
+ value=default_split,
241
+ label="Choose a split",
242
+ )
243
+ data_table = gr.Dataframe(label="First 10 rows", interactive=False)
244
+
245
+ split_selector.change(
246
+ fn=lambda s: show_data(s, splits_cache),
247
+ inputs=split_selector,
248
+ outputs=data_table,
249
+ )
250
+
251
+ demo.load(
252
+ fn=lambda: show_data(default_split, splits_cache),
253
+ inputs=[],
254
+ outputs=data_table,
255
+ )
256
+
257
+ with gr.TabItem("Model Demo"):
258
+ gr.Markdown(
259
+ """
260
+ Train a lightweight Random Forest baseline on the demo data and inspect predictions.
261
+ """
262
+ )
263
+ train_button = gr.Button("Train model")
264
+ plot_output = gr.Plot()
265
+ text_output = gr.Markdown()
266
+
267
+ train_button.click(
268
+ fn=lambda: train_model_demo(df_all),
269
+ inputs=[],
270
+ outputs=[plot_output, text_output],
271
+ )
272
+
273
+ with gr.TabItem("Noise Robustness Benchmark"):
274
+ gr.Markdown(
275
+ """
276
+ This tab loads the precomputed local benchmark results from `noise_benchmark_results.csv`.
277
+ """
278
+ )
279
+ refresh_button = gr.Button("Load benchmark results")
280
+ benchmark_table = gr.Dataframe(label="Benchmark results", interactive=False)
281
+ r2_plot = gr.Plot(label="R² plot")
282
+ mae_plot = gr.Plot(label="MAE plot")
283
+ benchmark_text = gr.Markdown()
284
+
285
+ refresh_button.click(
286
+ fn=build_benchmark_dashboard,
287
+ inputs=[],
288
+ outputs=[benchmark_table, r2_plot, mae_plot, benchmark_text],
289
+ )
290
+
291
+ demo.load(
292
+ fn=build_benchmark_dashboard,
293
+ inputs=[],
294
+ outputs=[benchmark_table, r2_plot, mae_plot, benchmark_text],
295
+ )
296
+
297
+ gr.Markdown("---")
298
+ gr.Markdown(
299
+ """
300
+ ### What this demo shows
301
+
302
+ - Data Explorer: inspect the dataset splits
303
+ - Model Demo: quick baseline on the demo data
304
+ - Noise Robustness Benchmark: precomputed results that show how performance changes across clean, noisy, and transpiled datasets
305
+ """
306
+ )
307
+
308
+ demo.launch()
309
+
310
+
311
+ if __name__ == "__main__":
312
+ main()