maskil commited on
Commit
eb52e7f
·
verified ·
1 Parent(s): 76c784d

Upload 2 files

Browse files
Files changed (2) hide show
  1. config.py +86 -0
  2. preprocessor.py +148 -0
config.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Central configuration for Rabi Oscillation Quality Classifier.
3
+ V2: Calibrated to real data distributions.
4
+ """
5
+ import torch
6
+ import os
7
+
8
+ # ─── Paths ───────────────────────────────────────────────────────────────────
9
+ PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
10
+ DATA_DIR = os.path.join(PROJECT_DIR, "data copy")
11
+ UI_TEST_DIR = os.path.join(PROJECT_DIR, "ui_test_samples")
12
+ MODEL_PATH = os.path.join(PROJECT_DIR, "best_model.pt")
13
+ CORRECTIONS_DIR = os.path.join(PROJECT_DIR, "data_corrections")
14
+ FAILED_PRED_DIR = os.path.join(PROJECT_DIR, "failed_predictions")
15
+
16
+ # ─── Device (MPS > CUDA > CPU) ──────────────────────────────────────────────
17
+ if torch.backends.mps.is_available():
18
+ DEVICE = torch.device("mps")
19
+ elif torch.cuda.is_available():
20
+ DEVICE = torch.device("cuda")
21
+ else:
22
+ DEVICE = torch.device("cpu")
23
+
24
+ # ─── Signal Preprocessing ───────────────────────────────────────────────────
25
+ SEQ_LEN = 256 # Increased from 128 for finer resolution
26
+ NUM_FIT_PARAMS = 4 # amplitude, T, phase, offset
27
+
28
+ # ─── Model ───────────────────────────────────────────────────────────────────
29
+ NUM_CLASSES = 3 # Classes per head: 0=Bad, 1=Acceptable, 2=Perfect
30
+ IN_CHANNELS = 3 # raw signal + fit curve + residual (signal - fit)
31
+ RESNET_DIM = 256 # ResNet backbone output dim
32
+ DENSE_DIM = 64 # Dense branch output dim
33
+ FUSED_DIM = RESNET_DIM + DENSE_DIM # 320
34
+
35
+ # ─── Training ────────────────────────────────────────────────────────────────
36
+ BATCH_SIZE = 256
37
+ LR = 3e-4 # Lower LR for better convergence
38
+ WEIGHT_DECAY = 1e-4
39
+ EPOCHS = 80 # More epochs
40
+ TRAIN_SIZE = 200_000 # Doubled for better generalization
41
+ VAL_SIZE = 40_000
42
+ PATIENCE = 15 # More patience
43
+ FOCAL_GAMMA = 2.0
44
+ CLASS_WEIGHTS = [1.2, 0.9, 0.9] # Stronger upweight for class 0
45
+ IGNORE_INDEX = -1
46
+
47
+ # ─── Synthetic Generator — Calibrated to Real Data ──────────────────────────
48
+ # Real data statistics (from analysis of 2736 JSON files):
49
+ # amplitude: mean=0.090, std=0.083, range=[~0, 0.40], p25=0.0002
50
+ # T: mean=0.101 but p75=0.062, most values 0.047-0.110
51
+ # phase: range=[-π, π]
52
+ # offset: mean=0.185, std=0.166, range=[~0, 0.98]
53
+ # x_range: mean=0.106, range=[0.006, 3.58]
54
+ # n_points: mean=145, range=[80, 1000]
55
+
56
+ # Data Quality SNR thresholds (dB)
57
+ SNR_PERFECT_MIN = 15.0 # Loosened from 20 (real data is noisier)
58
+ SNR_ACCEPTABLE_MIN = 5.0 # Loosened from 8
59
+ SNR_BORDERLINE_MIN = 3.0
60
+
61
+ # Fit Quality error thresholds (fraction)
62
+ FIT_PERFECT_MAX_ERR = 0.03
63
+ FIT_ACCEPTABLE_MAX_ERR = 0.20
64
+ FIT_BAD_MIN_ERR = 0.20
65
+ FIT_BORDERLINE_ERR_RANGE = (0.15, 0.30)
66
+
67
+ # Rabi oscillation parameter ranges — CALIBRATED TO REAL DATA
68
+ AMPLITUDE_RANGE = (0.0001, 0.42) # Real: [~0, 0.40]
69
+ PERIOD_RANGE = (0.025, 0.80) # Real: [0.025, 0.78] for good fits
70
+ PHASE_RANGE = (-3.1416, 3.1416) # Real: [-π, π]
71
+ OFFSET_RANGE = (0.0001, 0.98) # Real: [~0, 0.98]
72
+ DECAY_RANGE = (0.0, 30.0) # Reduced max decay
73
+ X_MAX_RANGE = (0.005, 0.50) # Real: [0.006, 3.58] but most < 0.2
74
+
75
+ # Extended ranges for diversity
76
+ X_MAX_EXTENDED = (0.50, 3.60) # For occasional long sweeps
77
+ PERIOD_BAD_RANGE = (0.002, 19.0) # Real bad fits have T up to 19
78
+
79
+ # Per-sample x-axis variation
80
+ N_POINTS_RANGE = (80, 500) # Real: [80, 1000]
81
+
82
+ # Proportion of borderline cases in Class 0
83
+ BORDERLINE_FRACTION = 0.5 # Increased from 0.4
84
+
85
+ # Proportion of near-zero amplitude samples (mimics ~25% of real data)
86
+ NEAR_ZERO_AMP_FRACTION = 0.20
preprocessor.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Preprocessor for Rabi oscillation JSON experiment files.
3
+ V3: Outputs 3 channels — signal + fit + residual (signal − fit).
4
+ """
5
+ import json
6
+ import numpy as np
7
+ import torch
8
+ from config import SEQ_LEN
9
+
10
+
11
+ def load_json(path: str) -> dict:
12
+ """Load a JSON experiment file."""
13
+ with open(path, 'r') as f:
14
+ return json.load(f)
15
+
16
+
17
+ def extract_fit_params(data: dict) -> dict:
18
+ """Extract the 4 fit parameters from JSON data.
19
+
20
+ Returns dict with keys: amplitude, T, phase, offset.
21
+ Returns zeros if fitted_data is missing or None.
22
+ """
23
+ defaults = {'amplitude': 0.0, 'T': 0.05, 'phase': 0.0, 'offset': 0.0}
24
+
25
+ fitted = data.get('fitted_data')
26
+ if fitted is None:
27
+ return defaults
28
+
29
+ params_list = fitted.get('parameters')
30
+ if params_list is None:
31
+ return defaults
32
+
33
+ params = dict(defaults)
34
+ for p in params_list:
35
+ if p.get('name') in params:
36
+ params[p['name']] = float(p.get('value', 0.0))
37
+ return params
38
+
39
+
40
+ def rabi_oscillation(x, amplitude, T, phase, offset):
41
+ """Compute Rabi oscillation curve (without decay, matching JSON fit)."""
42
+ return amplitude * np.cos((2 * np.pi / T) * x + phase) + offset
43
+
44
+
45
+ def reconstruct_fit_curve(x: np.ndarray, params: dict) -> np.ndarray:
46
+ """Reconstruct the fit curve on the original X-axis using JSON parameters."""
47
+ return rabi_oscillation(
48
+ x,
49
+ amplitude=params['amplitude'],
50
+ T=params['T'],
51
+ phase=params['phase'],
52
+ offset=params['offset']
53
+ )
54
+
55
+
56
+ def preprocess_sample(data: dict):
57
+ """
58
+ Preprocess a single JSON experiment sample.
59
+
60
+ V3: Returns 3-channel tensor (signal, fit, residual).
61
+
62
+ Returns:
63
+ signal_tensor: (3, SEQ_LEN) — [signal, fit, residual]
64
+ params_tensor: (4,) fit parameters [amplitude, T, phase, offset]
65
+ question_id: str ('q1' or 'q2')
66
+ score: float (0.0 or 1.0)
67
+ """
68
+ # Extract raw data
69
+ x_raw = np.array(data['measured_data']['x_values'], dtype=np.float64)
70
+ y_raw = np.array(data['measured_data']['y_values'], dtype=np.float64)
71
+
72
+ # Extract fit parameters
73
+ params = extract_fit_params(data)
74
+
75
+ # Reconstruct fit curve on original x-axis
76
+ y_fit = reconstruct_fit_curve(x_raw, params)
77
+
78
+ # Create uniform interpolation grid
79
+ x_uniform = np.linspace(x_raw.min(), x_raw.max(), SEQ_LEN)
80
+
81
+ # Interpolate both curves to fixed length
82
+ y_raw_interp = np.interp(x_uniform, x_raw, y_raw)
83
+ y_fit_interp = np.interp(x_uniform, x_raw, y_fit)
84
+
85
+ # Normalize both with same min-max scaling (from raw signal)
86
+ y_min = y_raw_interp.min()
87
+ y_max = y_raw_interp.max()
88
+ y_range = y_max - y_min
89
+ if y_range < 1e-10:
90
+ y_range = 1.0 # Avoid division by zero for flat signals
91
+
92
+ y_raw_norm = (y_raw_interp - y_min) / y_range
93
+ y_fit_norm = (y_fit_interp - y_min) / y_range
94
+
95
+ # Residual channel: signal − fit, normalized to [-1, 1]
96
+ residual = y_raw_norm - y_fit_norm
97
+ res_absmax = np.abs(residual).max()
98
+ if res_absmax < 1e-10:
99
+ res_absmax = 1.0
100
+ res_norm = residual / res_absmax
101
+
102
+ # Build 3-channel tensor
103
+ signal_tensor = torch.tensor(
104
+ np.stack([y_raw_norm, y_fit_norm, res_norm], axis=0),
105
+ dtype=torch.float32
106
+ )
107
+ params_tensor = torch.tensor(
108
+ [params['amplitude'], params['T'], params['phase'], params['offset']],
109
+ dtype=torch.float32
110
+ )
111
+
112
+ # Extract labels
113
+ question_id = data.get('question_id', 'q1')
114
+ score = float(data.get('score', 0.0))
115
+
116
+ return signal_tensor, params_tensor, question_id, score
117
+
118
+
119
+ def load_test_dataset(data_dir: str) -> list:
120
+ """
121
+ Load and preprocess all JSON files from the test data directory.
122
+
123
+ Returns list of dicts with keys:
124
+ signal, params, question_id, score, filename
125
+ """
126
+ import os
127
+ samples = []
128
+ json_files = sorted([f for f in os.listdir(data_dir) if f.endswith('.json')])
129
+
130
+ for fname in json_files:
131
+ path = os.path.join(data_dir, fname)
132
+ try:
133
+ data = load_json(path)
134
+ signal_3ch, params, qid, score = preprocess_sample(data)
135
+ samples.append({
136
+ 'signal': signal_3ch, # (3, SEQ_LEN)
137
+ 'params': params,
138
+ 'question_id': qid,
139
+ 'score': score,
140
+ 'filename': fname,
141
+ 'raw_data': data,
142
+ })
143
+ except Exception as e:
144
+ print(f"Warning: Failed to preprocess {fname}: {e}")
145
+ continue
146
+
147
+ print(f"Loaded {len(samples)} samples from {data_dir}")
148
+ return samples