QSBench commited on
Commit
0ca7d39
·
verified ·
1 Parent(s): c752e48

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -45
app.py CHANGED
@@ -1,5 +1,7 @@
1
  import ast
 
2
  import logging
 
3
  import re
4
  from typing import Dict, List, Optional, Tuple
5
 
@@ -7,43 +9,43 @@ import gradio as gr
7
  import matplotlib.pyplot as plt
8
  import numpy as np
9
  import pandas as pd
10
- from datasets import load_dataset
11
  from sklearn.ensemble import HistGradientBoostingClassifier
12
  from sklearn.impute import SimpleImputer
13
  from sklearn.inspection import permutation_importance
14
  from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, f1_score
15
  from sklearn.model_selection import train_test_split
16
  from sklearn.pipeline import Pipeline
17
- from sklearn.preprocessing import StandardScaler
18
 
19
  logging.basicConfig(level=logging.INFO)
20
  logger = logging.getLogger(__name__)
21
 
22
  APP_TITLE = "Noise Detection"
23
  APP_SUBTITLE = (
24
- "Classify quantum circuits into clean, depolarizing, amplitude_damping, or hardware-aware noise conditions."
25
  )
26
 
 
 
27
  REPO_CONFIG = {
28
  "clean": {
29
  "label": "clean",
30
- "repo": "QSBench/QSBench-Core-v1.0.0-demo",
31
  },
32
  "depolarizing": {
33
  "label": "depolarizing",
34
- "repo": "QSBench/QSBench-Depolarizing-Demo-v1.0.0",
35
  },
36
  "amplitude_damping": {
37
  "label": "amplitude_damping",
38
- "repo": "QSBench/QSBench-Amplitude-v1.0.0-demo",
39
  },
40
  "hardware_aware": {
41
  "label": "hardware_aware",
42
- "repo": "QSBench/QSBench-Transpilation-v1.0.0-demo",
43
  },
44
  }
45
 
46
- CLASS_ORDER = ["clean", "depolarizing", "amplitude_damping", "hardware_aware"]
47
 
48
  NON_FEATURE_COLS = {
49
  "sample_id",
@@ -66,12 +68,14 @@ NON_FEATURE_COLS = {
66
  "meyer_wallach",
67
  "cx_count",
68
  "noise_label",
 
 
69
  }
70
 
71
  SOFT_EXCLUDE_PATTERNS = ["ideal_", "noisy_", "error_", "sign_ideal_", "sign_noisy_"]
72
 
73
  _ASSET_CACHE: Dict[str, pd.DataFrame] = {}
74
- _COMBINED_CACHE: Optional[pd.DataFrame] = None
75
 
76
 
77
  def safe_parse(value):
@@ -94,6 +98,7 @@ def adjacency_features(adj_value) -> Dict[str, float]:
94
  "adj_degree_mean": np.nan,
95
  "adj_degree_std": np.nan,
96
  }
 
97
  try:
98
  arr = np.array(parsed, dtype=float)
99
  n = arr.shape[0]
@@ -126,6 +131,7 @@ def qasm_features(qasm_value) -> Dict[str, float]:
126
  "qasm_measure_count": np.nan,
127
  "qasm_comment_count": np.nan,
128
  }
 
129
  text = qasm_value
130
  lines = [line for line in text.splitlines() if line.strip()]
131
  gate_keywords = re.findall(
@@ -135,6 +141,7 @@ def qasm_features(qasm_value) -> Dict[str, float]:
135
  )
136
  measure_count = len(re.findall(r"\bmeasure\b", text, flags=re.IGNORECASE))
137
  comment_count = sum(1 for line in lines if line.strip().startswith("//"))
 
138
  return {
139
  "qasm_length": float(len(text)),
140
  "qasm_line_count": float(len(lines)),
@@ -147,37 +154,63 @@ def qasm_features(qasm_value) -> Dict[str, float]:
147
  def enrich_dataframe(df: pd.DataFrame) -> pd.DataFrame:
148
  """Add derived numeric features for classification."""
149
  df = df.copy()
 
150
  if "adjacency" in df.columns:
151
  adj_df = df["adjacency"].apply(adjacency_features).apply(pd.Series)
152
  df = pd.concat([df, adj_df], axis=1)
 
153
  qasm_source = "qasm_transpiled" if "qasm_transpiled" in df.columns else "qasm_raw"
154
  if qasm_source in df.columns:
155
  qasm_df = df[qasm_source].apply(qasm_features).apply(pd.Series)
156
  df = pd.concat([df, qasm_df], axis=1)
 
157
  return df
158
 
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  def load_single_dataset(dataset_key: str) -> pd.DataFrame:
161
- """Load a dataset shard from Hugging Face and cache it in memory."""
162
  if dataset_key not in _ASSET_CACHE:
163
- logger.info("Loading dataset: %s", dataset_key)
164
- ds = load_dataset(REPO_CONFIG[dataset_key]["repo"])
165
- df = pd.DataFrame(ds["train"])
166
  df = enrich_dataframe(df)
167
  df["noise_label"] = REPO_CONFIG[dataset_key]["label"]
 
168
  _ASSET_CACHE[dataset_key] = df
169
  return _ASSET_CACHE[dataset_key]
170
 
171
 
172
- def load_combined_dataset() -> pd.DataFrame:
173
- """Load and merge all four noise-condition datasets."""
174
- global _COMBINED_CACHE
175
- if _COMBINED_CACHE is None:
176
- frames = [load_single_dataset(key) for key in REPO_CONFIG.keys()]
177
  combined = pd.concat(frames, ignore_index=True)
178
- combined = combined[combined["noise_label"].isin(CLASS_ORDER)].copy()
179
- _COMBINED_CACHE = combined
180
- return _COMBINED_CACHE
181
 
182
 
183
  def load_guide_content() -> str:
@@ -232,6 +265,7 @@ def make_classification_figure(
232
  """Create a compact classification summary figure."""
233
  fig = plt.figure(figsize=(20, 6))
234
  gs = fig.add_gridspec(1, 3)
 
235
  ax1 = fig.add_subplot(gs[0, 0])
236
  ax2 = fig.add_subplot(gs[0, 1])
237
  ax3 = fig.add_subplot(gs[0, 2])
@@ -275,7 +309,7 @@ def build_dataset_profile(df: pd.DataFrame) -> str:
275
  f"### Dataset profile\n\n"
276
  f"**Rows:** {len(df):,} \n"
277
  f"**Columns:** {len(df.columns):,} \n"
278
- f"**Classes:** {', '.join(CLASS_ORDER)}"
279
  )
280
 
281
 
@@ -285,20 +319,26 @@ def refresh_explorer(dataset_key: str, split_name: str) -> Tuple[gr.update, pd.D
285
  splits = df["split"].dropna().unique().tolist() if "split" in df.columns else ["train"]
286
  if not splits:
287
  splits = ["train"]
 
288
  if split_name not in splits:
289
  split_name = splits[0]
 
290
  filtered = df[df["split"] == split_name] if "split" in df.columns else df
291
  display_df = filtered.head(12).copy()
 
292
  raw_qasm = display_df["qasm_raw"].iloc[0] if "qasm_raw" in display_df.columns and not display_df.empty else "// N/A"
293
  transpiled_qasm = display_df["qasm_transpiled"].iloc[0] if "qasm_transpiled" in display_df.columns and not display_df.empty else "// N/A"
 
294
  profile_box = build_dataset_profile(df)
295
  summary_box = (
296
  f"### Split summary\n\n"
297
  f"**Dataset:** `{dataset_key}` \n"
298
  f"**Label:** `{REPO_CONFIG[dataset_key]['label']}` \n"
 
299
  f"**Available splits:** {', '.join(splits)} \n"
300
  f"**Preview rows:** {len(display_df)}"
301
  )
 
302
  return (
303
  gr.update(choices=splits, value=split_name),
304
  display_df,
@@ -309,36 +349,50 @@ def refresh_explorer(dataset_key: str, split_name: str) -> Tuple[gr.update, pd.D
309
  )
310
 
311
 
312
- def sync_feature_picker(dataset_key: str) -> gr.update:
313
- """Refresh the feature list from the selected dataset."""
314
- df = load_single_dataset(dataset_key)
 
 
 
315
  features = get_available_feature_columns(df)
316
  defaults = default_feature_selection(features)
317
  return gr.update(choices=features, value=defaults)
318
 
319
 
320
  def train_classifier(
321
- dataset_key: str,
322
  feature_columns: List[str],
323
  test_size: float,
324
  n_estimators: int,
325
  max_depth: float,
326
  random_state: float,
327
  ) -> Tuple[Optional[plt.Figure], str]:
328
- """Train a four-class classifier with better handling of class imbalance."""
 
 
 
329
  if not feature_columns:
330
  return None, "### ❌ Please select at least one feature."
331
 
332
- df = load_single_dataset(dataset_key)
333
- required_cols = feature_columns + ["noise_label"]
334
- train_df = df.dropna(subset=required_cols).copy()
335
- train_df = train_df[train_df["noise_label"].isin(CLASS_ORDER)]
 
 
 
336
 
337
  if len(train_df) < 20:
338
  return None, "### ❌ Not enough rows after filtering missing values."
339
 
340
- X = train_df[feature_columns]
341
- y = train_df["noise_label"]
 
 
 
 
 
342
 
343
  seed = int(random_state)
344
  depth = int(max_depth) if max_depth and int(max_depth) > 0 else None
@@ -346,17 +400,23 @@ def train_classifier(
346
 
347
  try:
348
  X_train, X_test, y_train, y_test = train_test_split(
349
- X, y, test_size=test_size, random_state=seed, stratify=y
 
 
 
 
350
  )
351
  except ValueError:
352
  X_train, X_test, y_train, y_test = train_test_split(
353
- X, y, test_size=test_size, random_state=seed
 
 
 
354
  )
355
 
356
  model = Pipeline(
357
  steps=[
358
  ("imputer", SimpleImputer(strategy="median")),
359
- ("scaler", StandardScaler()),
360
  (
361
  "classifier",
362
  HistGradientBoostingClassifier(
@@ -390,7 +450,7 @@ def train_classifier(
390
  )
391
  importances = perm.importances_mean
392
 
393
- fig = make_classification_figure(y_test.to_numpy(), y_pred, CLASS_ORDER, list(feature_columns), importances)
394
 
395
  report = classification_report(
396
  y_test,
@@ -401,7 +461,7 @@ def train_classifier(
401
  results = (
402
  "### Classification results\n\n"
403
  f"**Rows used:** {len(train_df):,} \n"
404
- f"**Dataset:** `{dataset_key}` \n"
405
  f"**Test size:** {test_size:.0%} \n"
406
  f"**Accuracy:** {accuracy:.4f} \n"
407
  f"**Macro F1:** {macro_f1:.4f} \n"
@@ -447,10 +507,10 @@ with gr.Blocks(title=APP_TITLE) as demo:
447
  transpiled_qasm = gr.Code(label="Transpiled QASM", language=None)
448
 
449
  with gr.TabItem("🧠 Classification"):
450
- class_dataset_dropdown = gr.Dropdown(
451
- list(REPO_CONFIG.keys()),
452
- value="clean",
453
- label="Dataset",
454
  )
455
  feature_picker = gr.CheckboxGroup(label="Input features", choices=[])
456
  test_size = gr.Slider(0.1, 0.4, value=0.2, step=0.05, label="Test split")
@@ -483,11 +543,11 @@ with gr.Blocks(title=APP_TITLE) as demo:
483
  [split_dropdown, explorer_df, raw_qasm, transpiled_qasm, profile_box, summary_box],
484
  )
485
 
486
- class_dataset_dropdown.change(sync_feature_picker, [class_dataset_dropdown], [feature_picker])
487
 
488
  run_btn.click(
489
  train_classifier,
490
- [class_dataset_dropdown, feature_picker, test_size, n_estimators, max_depth, seed],
491
  [plot, metrics],
492
  )
493
 
@@ -496,7 +556,7 @@ with gr.Blocks(title=APP_TITLE) as demo:
496
  [dataset_dropdown, split_dropdown],
497
  [split_dropdown, explorer_df, raw_qasm, transpiled_qasm, profile_box, summary_box],
498
  )
499
- demo.load(sync_feature_picker, [class_dataset_dropdown], [feature_picker])
500
 
501
 
502
  if __name__ == "__main__":
 
1
  import ast
2
+ import glob
3
  import logging
4
+ import os
5
  import re
6
  from typing import Dict, List, Optional, Tuple
7
 
 
9
  import matplotlib.pyplot as plt
10
  import numpy as np
11
  import pandas as pd
 
12
  from sklearn.ensemble import HistGradientBoostingClassifier
13
  from sklearn.impute import SimpleImputer
14
  from sklearn.inspection import permutation_importance
15
  from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, f1_score
16
  from sklearn.model_selection import train_test_split
17
  from sklearn.pipeline import Pipeline
 
18
 
19
  logging.basicConfig(level=logging.INFO)
20
  logger = logging.getLogger(__name__)
21
 
22
  APP_TITLE = "Noise Detection"
23
  APP_SUBTITLE = (
24
+ "Detect hardware-aware transpilation artifacts versus all other circuit conditions using structural circuit features."
25
  )
26
 
27
+ DATA_DIR = os.getenv("QS_DATA_DIR", "data")
28
+
29
  REPO_CONFIG = {
30
  "clean": {
31
  "label": "clean",
32
+ "path": os.getenv("QS_CLEAN_PATH", os.path.join(DATA_DIR, "core")),
33
  },
34
  "depolarizing": {
35
  "label": "depolarizing",
36
+ "path": os.getenv("QS_DEPOLARIZING_PATH", os.path.join(DATA_DIR, "depolarizing")),
37
  },
38
  "amplitude_damping": {
39
  "label": "amplitude_damping",
40
+ "path": os.getenv("QS_AMPLITUDE_PATH", os.path.join(DATA_DIR, "amplitude")),
41
  },
42
  "hardware_aware": {
43
  "label": "hardware_aware",
44
+ "path": os.getenv("QS_HARDWARE_AWARE_PATH", os.path.join(DATA_DIR, "transpilation")),
45
  },
46
  }
47
 
48
+ CLASS_ORDER = ["other", "hardware_aware"]
49
 
50
  NON_FEATURE_COLS = {
51
  "sample_id",
 
68
  "meyer_wallach",
69
  "cx_count",
70
  "noise_label",
71
+ "source_dataset",
72
+ "target_label",
73
  }
74
 
75
  SOFT_EXCLUDE_PATTERNS = ["ideal_", "noisy_", "error_", "sign_ideal_", "sign_noisy_"]
76
 
77
  _ASSET_CACHE: Dict[str, pd.DataFrame] = {}
78
+ _COMBINED_CACHE: Dict[Tuple[str, ...], pd.DataFrame] = {}
79
 
80
 
81
  def safe_parse(value):
 
98
  "adj_degree_mean": np.nan,
99
  "adj_degree_std": np.nan,
100
  }
101
+
102
  try:
103
  arr = np.array(parsed, dtype=float)
104
  n = arr.shape[0]
 
131
  "qasm_measure_count": np.nan,
132
  "qasm_comment_count": np.nan,
133
  }
134
+
135
  text = qasm_value
136
  lines = [line for line in text.splitlines() if line.strip()]
137
  gate_keywords = re.findall(
 
141
  )
142
  measure_count = len(re.findall(r"\bmeasure\b", text, flags=re.IGNORECASE))
143
  comment_count = sum(1 for line in lines if line.strip().startswith("//"))
144
+
145
  return {
146
  "qasm_length": float(len(text)),
147
  "qasm_line_count": float(len(lines)),
 
154
  def enrich_dataframe(df: pd.DataFrame) -> pd.DataFrame:
155
  """Add derived numeric features for classification."""
156
  df = df.copy()
157
+
158
  if "adjacency" in df.columns:
159
  adj_df = df["adjacency"].apply(adjacency_features).apply(pd.Series)
160
  df = pd.concat([df, adj_df], axis=1)
161
+
162
  qasm_source = "qasm_transpiled" if "qasm_transpiled" in df.columns else "qasm_raw"
163
  if qasm_source in df.columns:
164
  qasm_df = df[qasm_source].apply(qasm_features).apply(pd.Series)
165
  df = pd.concat([df, qasm_df], axis=1)
166
+
167
  return df
168
 
169
 
170
+ def _resolve_path(dataset_key: str) -> str:
171
+ path = REPO_CONFIG[dataset_key]["path"]
172
+ if not os.path.exists(path):
173
+ raise FileNotFoundError(
174
+ f"Local dataset path not found for '{dataset_key}': {path}. "
175
+ "Set the matching environment variable or place the parquet directory at this path."
176
+ )
177
+ return path
178
+
179
+
180
+ def _read_parquet_source(path: str) -> pd.DataFrame:
181
+ """Read a parquet file or a directory of parquet shards."""
182
+ if os.path.isdir(path):
183
+ files = sorted(glob.glob(os.path.join(path, "*.parquet")))
184
+ if not files:
185
+ raise FileNotFoundError(f"No parquet files found in directory: {path}")
186
+ frames = [pd.read_parquet(file_path) for file_path in files]
187
+ return pd.concat(frames, ignore_index=True)
188
+
189
+ return pd.read_parquet(path)
190
+
191
+
192
  def load_single_dataset(dataset_key: str) -> pd.DataFrame:
193
+ """Load a local parquet dataset and cache it in memory."""
194
  if dataset_key not in _ASSET_CACHE:
195
+ path = _resolve_path(dataset_key)
196
+ logger.info("Loading local dataset: %s -> %s", dataset_key, path)
197
+ df = _read_parquet_source(path)
198
  df = enrich_dataframe(df)
199
  df["noise_label"] = REPO_CONFIG[dataset_key]["label"]
200
+ df["source_dataset"] = dataset_key
201
  _ASSET_CACHE[dataset_key] = df
202
  return _ASSET_CACHE[dataset_key]
203
 
204
 
205
+ def load_combined_dataset(dataset_keys: List[str]) -> pd.DataFrame:
206
+ """Load and merge selected local datasets."""
207
+ cache_key = tuple(sorted(dataset_keys))
208
+ if cache_key not in _COMBINED_CACHE:
209
+ frames = [load_single_dataset(key) for key in dataset_keys]
210
  combined = pd.concat(frames, ignore_index=True)
211
+ combined = combined.copy()
212
+ _COMBINED_CACHE[cache_key] = combined
213
+ return _COMBINED_CACHE[cache_key]
214
 
215
 
216
  def load_guide_content() -> str:
 
265
  """Create a compact classification summary figure."""
266
  fig = plt.figure(figsize=(20, 6))
267
  gs = fig.add_gridspec(1, 3)
268
+
269
  ax1 = fig.add_subplot(gs[0, 0])
270
  ax2 = fig.add_subplot(gs[0, 1])
271
  ax3 = fig.add_subplot(gs[0, 2])
 
309
  f"### Dataset profile\n\n"
310
  f"**Rows:** {len(df):,} \n"
311
  f"**Columns:** {len(df.columns):,} \n"
312
+ f"**Source label:** `{df['noise_label'].iloc[0] if 'noise_label' in df.columns and not df.empty else 'n/a'}`"
313
  )
314
 
315
 
 
319
  splits = df["split"].dropna().unique().tolist() if "split" in df.columns else ["train"]
320
  if not splits:
321
  splits = ["train"]
322
+
323
  if split_name not in splits:
324
  split_name = splits[0]
325
+
326
  filtered = df[df["split"] == split_name] if "split" in df.columns else df
327
  display_df = filtered.head(12).copy()
328
+
329
  raw_qasm = display_df["qasm_raw"].iloc[0] if "qasm_raw" in display_df.columns and not display_df.empty else "// N/A"
330
  transpiled_qasm = display_df["qasm_transpiled"].iloc[0] if "qasm_transpiled" in display_df.columns and not display_df.empty else "// N/A"
331
+
332
  profile_box = build_dataset_profile(df)
333
  summary_box = (
334
  f"### Split summary\n\n"
335
  f"**Dataset:** `{dataset_key}` \n"
336
  f"**Label:** `{REPO_CONFIG[dataset_key]['label']}` \n"
337
+ f"**Path:** `{REPO_CONFIG[dataset_key]['path']}` \n"
338
  f"**Available splits:** {', '.join(splits)} \n"
339
  f"**Preview rows:** {len(display_df)}"
340
  )
341
+
342
  return (
343
  gr.update(choices=splits, value=split_name),
344
  display_df,
 
349
  )
350
 
351
 
352
+ def sync_feature_picker(dataset_keys: List[str]) -> gr.update:
353
+ """Refresh the feature list from the selected datasets."""
354
+ if not dataset_keys:
355
+ return gr.update(choices=[], value=[])
356
+
357
+ df = load_combined_dataset(dataset_keys)
358
  features = get_available_feature_columns(df)
359
  defaults = default_feature_selection(features)
360
  return gr.update(choices=features, value=defaults)
361
 
362
 
363
  def train_classifier(
364
+ dataset_keys: List[str],
365
  feature_columns: List[str],
366
  test_size: float,
367
  n_estimators: int,
368
  max_depth: float,
369
  random_state: float,
370
  ) -> Tuple[Optional[plt.Figure], str]:
371
+ """Train a binary classifier for hardware-aware detection."""
372
+ if not dataset_keys:
373
+ return None, "### ❌ Please select at least one dataset."
374
+
375
  if not feature_columns:
376
  return None, "### ❌ Please select at least one feature."
377
 
378
+ df = load_combined_dataset(dataset_keys).copy()
379
+ df["target_label"] = np.where(df["source_dataset"] == "hardware_aware", "hardware_aware", "other")
380
+
381
+ if "target_label" not in df.columns:
382
+ return None, "### ❌ Target label could not be created."
383
+
384
+ train_df = df.dropna(subset=["target_label"]).copy()
385
 
386
  if len(train_df) < 20:
387
  return None, "### ❌ Not enough rows after filtering missing values."
388
 
389
+ X = train_df[feature_columns].copy()
390
+ X = X.dropna(axis=1, how="all")
391
+ if X.shape[1] == 0:
392
+ return None, "### ❌ All selected features are empty in the chosen datasets."
393
+
394
+ feature_columns = X.columns.tolist()
395
+ y = train_df["target_label"]
396
 
397
  seed = int(random_state)
398
  depth = int(max_depth) if max_depth and int(max_depth) > 0 else None
 
400
 
401
  try:
402
  X_train, X_test, y_train, y_test = train_test_split(
403
+ X,
404
+ y,
405
+ test_size=test_size,
406
+ random_state=seed,
407
+ stratify=y,
408
  )
409
  except ValueError:
410
  X_train, X_test, y_train, y_test = train_test_split(
411
+ X,
412
+ y,
413
+ test_size=test_size,
414
+ random_state=seed,
415
  )
416
 
417
  model = Pipeline(
418
  steps=[
419
  ("imputer", SimpleImputer(strategy="median")),
 
420
  (
421
  "classifier",
422
  HistGradientBoostingClassifier(
 
450
  )
451
  importances = perm.importances_mean
452
 
453
+ fig = make_classification_figure(y_test.to_numpy(), y_pred, CLASS_ORDER, feature_columns, importances)
454
 
455
  report = classification_report(
456
  y_test,
 
461
  results = (
462
  "### Classification results\n\n"
463
  f"**Rows used:** {len(train_df):,} \n"
464
+ f"**Datasets used:** {', '.join(dataset_keys)} \n"
465
  f"**Test size:** {test_size:.0%} \n"
466
  f"**Accuracy:** {accuracy:.4f} \n"
467
  f"**Macro F1:** {macro_f1:.4f} \n"
 
507
  transpiled_qasm = gr.Code(label="Transpiled QASM", language=None)
508
 
509
  with gr.TabItem("🧠 Classification"):
510
+ class_dataset_picker = gr.CheckboxGroup(
511
+ label="Datasets",
512
+ choices=list(REPO_CONFIG.keys()),
513
+ value=list(REPO_CONFIG.keys()),
514
  )
515
  feature_picker = gr.CheckboxGroup(label="Input features", choices=[])
516
  test_size = gr.Slider(0.1, 0.4, value=0.2, step=0.05, label="Test split")
 
543
  [split_dropdown, explorer_df, raw_qasm, transpiled_qasm, profile_box, summary_box],
544
  )
545
 
546
+ class_dataset_picker.change(sync_feature_picker, [class_dataset_picker], [feature_picker])
547
 
548
  run_btn.click(
549
  train_classifier,
550
+ [class_dataset_picker, feature_picker, test_size, n_estimators, max_depth, seed],
551
  [plot, metrics],
552
  )
553
 
 
556
  [dataset_dropdown, split_dropdown],
557
  [split_dropdown, explorer_df, raw_qasm, transpiled_qasm, profile_box, summary_box],
558
  )
559
+ demo.load(sync_feature_picker, [class_dataset_picker], [feature_picker])
560
 
561
 
562
  if __name__ == "__main__":