qcoder 0.1.0a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qcoder/__init__.py +3 -0
- qcoder/__main__.py +6 -0
- qcoder/cli.py +116 -0
- qcoder/core/__init__.py +1 -0
- qcoder/core/context.py +16 -0
- qcoder/core/qasm2/__init__.py +1 -0
- qcoder/core/qasm2/adjoint_eligibility.py +128 -0
- qcoder/core/qasm2/mirror_build.py +234 -0
- qcoder/core/run_config.py +84 -0
- qcoder/core/schema.py +26 -0
- qcoder/engines/feature_extraction/adapters/__init__.py +1 -0
- qcoder/engines/feature_extraction/adapters/qiskit_intake.py +46 -0
- qcoder/engines/feature_extraction/extractor.py +43 -0
- qcoder/engines/feature_extraction/features/compute_v0.py +157 -0
- qcoder/engines/feature_extraction/features/schema_v0.py +84 -0
- qcoder/engines/feature_extraction/ir.py +41 -0
- qcoder/engines/feature_extraction/labeling.py +68 -0
- qcoder/engines/feature_extraction/parsers/__init__.py +21 -0
- qcoder/engines/feature_extraction/qasm2_regex_parser.py +184 -0
- qcoder/engines/feature_extraction/reps/cut_profile.py +106 -0
- qcoder/engines/feature_extraction/reps/depth.py +47 -0
- qcoder/engines/feature_extraction/reps/entangling_layers.py +57 -0
- qcoder/engines/feature_extraction/reps/gate_set_stats.py +82 -0
- qcoder/engines/feature_extraction/reps/interaction_graph.py +30 -0
- qcoder/engines/feature_extraction/reps/interaction_graph_metrics.py +113 -0
- qcoder/engines/feature_extraction/reps/spans.py +89 -0
- qcoder/engines/prediction_model/__init__.py +16 -0
- qcoder/engines/prediction_model/artifact.py +85 -0
- qcoder/engines/prediction_model/engine.py +209 -0
- qcoder/engines/prediction_model/models.py +62 -0
- qcoder/engines/prediction_model/policy.py +45 -0
- qcoder/engines/prediction_model/schema_alignment.py +41 -0
- qcoder/engines/quantumness/__init__.py +8 -0
- qcoder/engines/quantumness/scorer.py +254 -0
- qcoder/pipelines/analyze.py +131 -0
- qcoder/pipelines/batch.py +56 -0
- qcoder/tools/analyze.py +88 -0
- qcoder/tools/analyze_shot_scaling.py +239 -0
- qcoder/tools/batch.py +39 -0
- qcoder/tools/generate_corpus.py +491 -0
- qcoder/tools/harness.py +15 -0
- qcoder/tools/inspect_corpus_features.py +273 -0
- qcoder/tools/join_runs_features.py +252 -0
- qcoder/tools/mirror.py +15 -0
- qcoder/tools/predict_baseline.py +347 -0
- qcoder/tools/qr_dll_bootstrap.py +31 -0
- qcoder/tools/runner.py +15 -0
- qcoder/tools/runners/__init__.py +1 -0
- qcoder/tools/runners/quantum_rings/__init__.py +1 -0
- qcoder/tools/runners/quantum_rings/v12/__init__.py +1 -0
- qcoder/tools/runners/quantum_rings/v12/harness.py +1350 -0
- qcoder/tools/runners/quantum_rings/v12/mirror.py +459 -0
- qcoder/tools/runners/quantum_rings/v12/runner.py +549 -0
- qcoder/tools/train_baseline_models.py +619 -0
- qcoder/tools/validate_baseline.py +307 -0
- qcoder-0.1.0a0.dist-info/METADATA +86 -0
- qcoder-0.1.0a0.dist-info/RECORD +62 -0
- qcoder-0.1.0a0.dist-info/WHEEL +5 -0
- qcoder-0.1.0a0.dist-info/entry_points.txt +2 -0
- qcoder-0.1.0a0.dist-info/licenses/LICENSE +201 -0
- qcoder-0.1.0a0.dist-info/licenses/NOTICE +11 -0
- qcoder-0.1.0a0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
qcoder.tools.mirror
|
|
4
|
+
|
|
5
|
+
Mirror runner:
|
|
6
|
+
- Builds mirror QASM (U then U†) using qcoder.core.qasm2.mirror_build
|
|
7
|
+
- Loads mirror circuit in QuantumRingsLib
|
|
8
|
+
- Runs with measurements
|
|
9
|
+
- Computes mirror "success probability": P(measuring all-zeros) from counts
|
|
10
|
+
- Records wall time + peak RSS (external sampler)
|
|
11
|
+
- Records SDK-native metrics best-effort:
|
|
12
|
+
- result.get_fidelity() (semantics TBD; logged for analysis)
|
|
13
|
+
- result.get_peakmemorysize(0) (signature requires an int)
|
|
14
|
+
- result.get_memory()
|
|
15
|
+
|
|
16
|
+
Why counts-based mirror?
|
|
17
|
+
- Venkat says get_amplitude is not a published API; do not use.
|
|
18
|
+
- Counts-based "return-to-zero probability" is a standard mirror-style metric.
|
|
19
|
+
|
|
20
|
+
NOTE:
|
|
21
|
+
- This is NOT guaranteed to correlate perfectly with forward-distribution fidelity,
|
|
22
|
+
but it is at least well-defined and doesn’t rely on unpublished amplitude APIs.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from qcoder.tools import qr_dll_bootstrap
|
|
26
|
+
|
|
27
|
+
qr_dll_bootstrap.bootstrap()
|
|
28
|
+
|
|
29
|
+
import argparse
|
|
30
|
+
import datetime as _dt
|
|
31
|
+
import hashlib
|
|
32
|
+
import importlib.metadata as _imd
|
|
33
|
+
import json
|
|
34
|
+
import os
|
|
35
|
+
import platform
|
|
36
|
+
import re
|
|
37
|
+
import subprocess
|
|
38
|
+
import sys
|
|
39
|
+
import time
|
|
40
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
41
|
+
|
|
42
|
+
import psutil
|
|
43
|
+
|
|
44
|
+
from QuantumRingsLib import QuantumRingsProvider, QuantumCircuit, job_monitor
|
|
45
|
+
|
|
46
|
+
from qcoder.core.qasm2.mirror_build import build_mirror_qasm, UnsupportedQasm
|
|
47
|
+
from qcoder.core.qasm2.adjoint_eligibility import classify_mirror_eligibility
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# -----------------------------
|
|
51
|
+
# Helpers
|
|
52
|
+
# -----------------------------
|
|
53
|
+
|
|
54
|
+
def die(msg: str, code: int = 1) -> None:
|
|
55
|
+
print(f"[fatal] {msg}", file=sys.stderr, flush=True)
|
|
56
|
+
sys.exit(code)
|
|
57
|
+
|
|
58
|
+
def sha256_file(path: str) -> str:
|
|
59
|
+
h = hashlib.sha256()
|
|
60
|
+
with open(path, "rb") as f:
|
|
61
|
+
while True:
|
|
62
|
+
b = f.read(1 << 20)
|
|
63
|
+
if not b:
|
|
64
|
+
break
|
|
65
|
+
h.update(b)
|
|
66
|
+
return h.hexdigest()
|
|
67
|
+
|
|
68
|
+
def resolve_backend_id(provider: QuantumRingsProvider, backend_arg: str, precision: str) -> str:
|
|
69
|
+
"""
|
|
70
|
+
Accept friendly names (Scarlet/Amber/Serin or CPU/GPU/Both) OR raw backend ids.
|
|
71
|
+
"""
|
|
72
|
+
if not backend_arg:
|
|
73
|
+
return "scarlet_quantum_rings"
|
|
74
|
+
|
|
75
|
+
b = backend_arg.strip()
|
|
76
|
+
if "_" in b or b.endswith("_quantum_rings"):
|
|
77
|
+
return b
|
|
78
|
+
|
|
79
|
+
key = b.lower()
|
|
80
|
+
if key in ("scarlet", "cpu"):
|
|
81
|
+
candidates = ["scarlet_quantum_rings", "scarlet"]
|
|
82
|
+
elif key in ("amber", "gpu"):
|
|
83
|
+
candidates = ["amber_quantum_rings", "amber"]
|
|
84
|
+
elif key in ("serin", "both", "hybrid"):
|
|
85
|
+
candidates = ["serin_quantum_rings", "serin"]
|
|
86
|
+
else:
|
|
87
|
+
candidates = [b]
|
|
88
|
+
|
|
89
|
+
last_err = None
|
|
90
|
+
for cid in candidates:
|
|
91
|
+
try:
|
|
92
|
+
_ = provider.get_backend(cid, precision=precision)
|
|
93
|
+
return cid
|
|
94
|
+
except Exception as e:
|
|
95
|
+
last_err = e
|
|
96
|
+
die(f"Could not resolve backend '{backend_arg}'. Tried: {candidates}. Last error: {last_err}")
|
|
97
|
+
return b # unreachable
|
|
98
|
+
|
|
99
|
+
def default_json_out(qasm_path: str, backend_id: str, precision: str, threshold: int, shots: int, out_dir: str) -> str:
|
|
100
|
+
base = os.path.splitext(os.path.basename(qasm_path))[0]
|
|
101
|
+
fname = f"{base}__MIRROR__{backend_id}__{precision}__thr{threshold}__shots{shots}.json"
|
|
102
|
+
return os.path.join(out_dir, fname)
|
|
103
|
+
|
|
104
|
+
def try_nvidia_smi() -> Optional[Dict[str, Any]]:
|
|
105
|
+
try:
|
|
106
|
+
cmd = [
|
|
107
|
+
"nvidia-smi",
|
|
108
|
+
"--query-gpu=name,driver_version,memory.total",
|
|
109
|
+
"--format=csv,noheader,nounits",
|
|
110
|
+
]
|
|
111
|
+
p = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
112
|
+
if p.returncode != 0:
|
|
113
|
+
return None
|
|
114
|
+
line = (p.stdout.strip().splitlines() or [""])[0].strip()
|
|
115
|
+
if not line:
|
|
116
|
+
return None
|
|
117
|
+
parts = [x.strip() for x in line.split(",")]
|
|
118
|
+
if len(parts) >= 3:
|
|
119
|
+
return {
|
|
120
|
+
"gpu_name": parts[0],
|
|
121
|
+
"driver_version": parts[1],
|
|
122
|
+
"gpu_memory_total_mb": float(parts[2]) if parts[2].replace(".", "", 1).isdigit() else parts[2],
|
|
123
|
+
}
|
|
124
|
+
return {"raw": line}
|
|
125
|
+
except Exception:
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
def start_peak_rss_sampler(interval_s: float = 0.05):
|
|
129
|
+
import threading
|
|
130
|
+
import time as _time
|
|
131
|
+
import os as _os
|
|
132
|
+
|
|
133
|
+
proc = psutil.Process(_os.getpid())
|
|
134
|
+
peak = {"rss": 0}
|
|
135
|
+
running = {"on": True}
|
|
136
|
+
|
|
137
|
+
def _loop():
|
|
138
|
+
while running["on"]:
|
|
139
|
+
try:
|
|
140
|
+
rss = proc.memory_info().rss
|
|
141
|
+
if rss > peak["rss"]:
|
|
142
|
+
peak["rss"] = rss
|
|
143
|
+
except Exception:
|
|
144
|
+
pass
|
|
145
|
+
_time.sleep(interval_s)
|
|
146
|
+
|
|
147
|
+
th = threading.Thread(target=_loop, daemon=True)
|
|
148
|
+
th.start()
|
|
149
|
+
return running, th, peak
|
|
150
|
+
|
|
151
|
+
def stop_peak_rss_sampler(running, th):
|
|
152
|
+
running["on"] = False
|
|
153
|
+
th.join(timeout=1.0)
|
|
154
|
+
|
|
155
|
+
def get_dist_version(names: List[str]) -> Optional[str]:
|
|
156
|
+
for n in names:
|
|
157
|
+
try:
|
|
158
|
+
return _imd.version(n)
|
|
159
|
+
except Exception:
|
|
160
|
+
pass
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
def safe_call_noarg(result: Any, name: str):
|
|
164
|
+
if not hasattr(result, name):
|
|
165
|
+
return None, "missing"
|
|
166
|
+
try:
|
|
167
|
+
return getattr(result, name)(), None
|
|
168
|
+
except Exception as e:
|
|
169
|
+
return None, repr(e)
|
|
170
|
+
|
|
171
|
+
def safe_call_1arg(result: Any, name: str, arg0: int):
|
|
172
|
+
if not hasattr(result, name):
|
|
173
|
+
return None, "missing"
|
|
174
|
+
try:
|
|
175
|
+
return getattr(result, name)(arg0), None
|
|
176
|
+
except Exception as e:
|
|
177
|
+
return None, repr(e)
|
|
178
|
+
|
|
179
|
+
def jsonable_value(x: Any) -> Any:
|
|
180
|
+
if x is None:
|
|
181
|
+
return None
|
|
182
|
+
if isinstance(x, (str, int, float, bool)):
|
|
183
|
+
return x
|
|
184
|
+
return repr(x)
|
|
185
|
+
|
|
186
|
+
def normalize_counts_keys(counts: Dict[Any, Any]) -> Dict[str, int]:
|
|
187
|
+
out: Dict[str, int] = {}
|
|
188
|
+
for k, v in counts.items():
|
|
189
|
+
ks = str(k).replace(" ", "")
|
|
190
|
+
out[ks] = int(v)
|
|
191
|
+
return out
|
|
192
|
+
|
|
193
|
+
_MEASURE_LINE_RE = re.compile(r"^\s*measure\s+.*?;\s*$", flags=re.M | re.I)
|
|
194
|
+
_CREG_DECL_RE = re.compile(r"^\s*creg\s+.*?;\s*$", flags=re.M | re.I)
|
|
195
|
+
|
|
196
|
+
def strip_measures_and_cregs(qasm: str) -> str:
|
|
197
|
+
"""
|
|
198
|
+
Make QASM unitary-ish by removing creg declarations and measure lines.
|
|
199
|
+
This is required for qc.inverse() to work reliably.
|
|
200
|
+
"""
|
|
201
|
+
q = _MEASURE_LINE_RE.sub("", qasm)
|
|
202
|
+
q = _CREG_DECL_RE.sub("", q)
|
|
203
|
+
return q
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# -----------------------------
|
|
208
|
+
# Main
|
|
209
|
+
# -----------------------------
|
|
210
|
+
|
|
211
|
+
def main() -> None:
|
|
212
|
+
ap = argparse.ArgumentParser(description="QR12 mirror runner: mirror QASM + counts-based return-to-zero probability.")
|
|
213
|
+
ap.add_argument("qasm", help="Path to original QASM2 file (U)")
|
|
214
|
+
ap.add_argument("--backend", default="Scarlet", help="Scarlet|Amber|Serin (or CPU/GPU/Both) OR backend id")
|
|
215
|
+
ap.add_argument("--precision", choices=["single", "double"], default="single")
|
|
216
|
+
ap.add_argument("--threshold", type=int, default=128)
|
|
217
|
+
ap.add_argument("--shots", type=int, default=1024,
|
|
218
|
+
help="Shots for mirror counts. (Unlike amplitude-based mirror, you usually want >1.)")
|
|
219
|
+
ap.add_argument("--json-out", default=None)
|
|
220
|
+
ap.add_argument("--out-dir", default="runs")
|
|
221
|
+
ap.add_argument("--save-mirror-qasm", default=None, help="Optional: save generated mirror QASM for debugging")
|
|
222
|
+
ap.add_argument("--topk", type=int, default=20, help="Store top-K outcomes in the JSON")
|
|
223
|
+
ap.add_argument(
|
|
224
|
+
"--mirror-mode",
|
|
225
|
+
choices=["qasm_counts", "sdk_inverse_fidelity"],
|
|
226
|
+
default="qasm_counts",
|
|
227
|
+
help="qasm_counts: build mirror via qcoder.core.qasm2.mirror_build + measurements + p_return_zero. "
|
|
228
|
+
"sdk_inverse_fidelity: build mirror via qc.inverse()+append and use result.get_fidelity()."
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
args = ap.parse_args()
|
|
232
|
+
|
|
233
|
+
print("===== QR12 Mirror Runner =====", flush=True)
|
|
234
|
+
|
|
235
|
+
os.makedirs(args.out_dir, exist_ok=True)
|
|
236
|
+
|
|
237
|
+
sdk_pkg = "quantumrings-cuda13x"
|
|
238
|
+
sdk_ver = get_dist_version([sdk_pkg, "quantumrings_cuda13x", "QuantumRingsLib"])
|
|
239
|
+
print(f"[env] sdk_pkg={sdk_pkg} sdk_version={sdk_ver} python={sys.version.split()[0]}", flush=True)
|
|
240
|
+
|
|
241
|
+
# Provider first (important for entitlement context)
|
|
242
|
+
provider = QuantumRingsProvider()
|
|
243
|
+
backend_id = resolve_backend_id(provider, args.backend, args.precision)
|
|
244
|
+
backend = provider.get_backend(backend_id, precision=args.precision)
|
|
245
|
+
print("[backend]", backend, flush=True)
|
|
246
|
+
|
|
247
|
+
# Read original QASM
|
|
248
|
+
with open(args.qasm, "r", encoding="utf-8", errors="replace") as f:
|
|
249
|
+
orig_text = f.read()
|
|
250
|
+
eligibility, eligibility_reason = classify_mirror_eligibility(orig_text)
|
|
251
|
+
if eligibility != "ok":
|
|
252
|
+
die(f"Mirror eligibility={eligibility}: {eligibility_reason}", code=2)
|
|
253
|
+
|
|
254
|
+
# ---- Build mirror circuit (two modes) ----
|
|
255
|
+
qc_mirror = None
|
|
256
|
+
n_qubits = None
|
|
257
|
+
mirror_qasm = None # only set in qasm_counts
|
|
258
|
+
|
|
259
|
+
if args.mirror_mode == "sdk_inverse_fidelity":
|
|
260
|
+
# Venkat-style: unitary circuit -> inverse -> append
|
|
261
|
+
unitary_qasm = strip_measures_and_cregs(orig_text)
|
|
262
|
+
try:
|
|
263
|
+
qc_u = QuantumCircuit.from_qasm_str(unitary_qasm)
|
|
264
|
+
except Exception as e:
|
|
265
|
+
die(f"Unitary QASM failed to load for sdk_inverse_fidelity: {e}", code=3)
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
qc_inv = qc_u.inverse()
|
|
269
|
+
qc_u.append(qc_inv)
|
|
270
|
+
except Exception as e:
|
|
271
|
+
die(f"qc.inverse()/append failed in sdk_inverse_fidelity: {e}", code=4)
|
|
272
|
+
|
|
273
|
+
qc_mirror = qc_u
|
|
274
|
+
try:
|
|
275
|
+
n_qubits = int(qc_mirror.num_qubits)
|
|
276
|
+
except Exception:
|
|
277
|
+
n_qubits = None
|
|
278
|
+
|
|
279
|
+
# Venkat-like run kwargs (keep simple)
|
|
280
|
+
run_kwargs = dict(
|
|
281
|
+
shots=int(args.shots),
|
|
282
|
+
mode="async",
|
|
283
|
+
quiet=True,
|
|
284
|
+
performance="custom",
|
|
285
|
+
threshold=int(args.threshold),
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
else:
|
|
289
|
+
# Existing method: QASM mirror + measurements + counts-based p_return_zero
|
|
290
|
+
try:
|
|
291
|
+
mirror_qasm, n_qubits = build_mirror_qasm(orig_text, drop_barriers=True)
|
|
292
|
+
except UnsupportedQasm as e:
|
|
293
|
+
die(f"Unsupported QASM for mirror inversion: {e}", code=2)
|
|
294
|
+
|
|
295
|
+
if args.save_mirror_qasm:
|
|
296
|
+
os.makedirs(os.path.dirname(args.save_mirror_qasm) or ".", exist_ok=True)
|
|
297
|
+
with open(args.save_mirror_qasm, "w", encoding="utf-8") as f:
|
|
298
|
+
f.write(mirror_qasm)
|
|
299
|
+
print(f"[ok] wrote mirror QASM: {args.save_mirror_qasm}", flush=True)
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
qc_mirror = QuantumCircuit.from_qasm_str(mirror_qasm)
|
|
303
|
+
except Exception as e:
|
|
304
|
+
die(f"Mirror QASM failed to load in SDK: {e}", code=3)
|
|
305
|
+
|
|
306
|
+
run_kwargs = dict(
|
|
307
|
+
shots=int(args.shots),
|
|
308
|
+
mode="sync",
|
|
309
|
+
generate_amplitude=False,
|
|
310
|
+
quiet=True,
|
|
311
|
+
performance="custom",
|
|
312
|
+
threshold=int(args.threshold),
|
|
313
|
+
transfer_to_cpu=True,
|
|
314
|
+
max_threads=5,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
print(f"[run] backend_id={backend_id} precision={args.precision} thr={args.threshold} shots={args.shots}", flush=True)
|
|
319
|
+
|
|
320
|
+
running, th, peak = start_peak_rss_sampler(0.05)
|
|
321
|
+
t_submit = time.perf_counter()
|
|
322
|
+
|
|
323
|
+
job = backend.run(qc_mirror, **run_kwargs)
|
|
324
|
+
job_monitor(job, quiet=True)
|
|
325
|
+
result = job.result()
|
|
326
|
+
|
|
327
|
+
t_done = time.perf_counter()
|
|
328
|
+
stop_peak_rss_sampler(running, th)
|
|
329
|
+
|
|
330
|
+
run_wall_s = t_done - t_submit
|
|
331
|
+
peak_rss_bytes = int(peak["rss"])
|
|
332
|
+
|
|
333
|
+
# Mirror metrics
|
|
334
|
+
p_zero = None
|
|
335
|
+
shots_from_counts = 0
|
|
336
|
+
items = []
|
|
337
|
+
top_items = []
|
|
338
|
+
|
|
339
|
+
zero_key = None
|
|
340
|
+
try:
|
|
341
|
+
if n_qubits is not None:
|
|
342
|
+
zero_key = "0" * int(n_qubits)
|
|
343
|
+
except Exception:
|
|
344
|
+
zero_key = None
|
|
345
|
+
|
|
346
|
+
if args.mirror_mode == "qasm_counts":
|
|
347
|
+
counts_raw = result.get_counts()
|
|
348
|
+
if not isinstance(counts_raw, dict):
|
|
349
|
+
die(f"result.get_counts() returned unexpected type: {type(counts_raw)}")
|
|
350
|
+
|
|
351
|
+
counts = normalize_counts_keys(counts_raw)
|
|
352
|
+
shots_from_counts = sum(counts.values()) if counts else 0
|
|
353
|
+
|
|
354
|
+
if n_qubits is not None:
|
|
355
|
+
zero_key = "0" * int(n_qubits)
|
|
356
|
+
p_zero = (counts.get(zero_key, 0) / shots_from_counts) if shots_from_counts else None
|
|
357
|
+
|
|
358
|
+
items = sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))
|
|
359
|
+
topk = max(1, int(args.topk))
|
|
360
|
+
top_items = items[:topk]
|
|
361
|
+
else:
|
|
362
|
+
# sdk_inverse_fidelity: no measurements, so no counts-based p_return_zero
|
|
363
|
+
pass
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# SDK-native metrics (best-effort)
|
|
367
|
+
sdk_metrics: Dict[str, Any] = {"errors": {}}
|
|
368
|
+
|
|
369
|
+
gf, gf_err = safe_call_noarg(result, "get_fidelity")
|
|
370
|
+
if gf_err:
|
|
371
|
+
sdk_metrics["errors"]["get_fidelity"] = gf_err
|
|
372
|
+
sdk_metrics["get_fidelity"] = jsonable_value(gf)
|
|
373
|
+
|
|
374
|
+
# IMPORTANT: signature requires one int arg
|
|
375
|
+
gpm0, gpm0_err = safe_call_1arg(result, "get_peakmemorysize", 0)
|
|
376
|
+
if gpm0_err:
|
|
377
|
+
sdk_metrics["errors"]["get_peakmemorysize(0)"] = gpm0_err
|
|
378
|
+
sdk_metrics["get_peakmemorysize_0"] = jsonable_value(gpm0)
|
|
379
|
+
|
|
380
|
+
memx, memx_err = safe_call_noarg(result, "get_memory")
|
|
381
|
+
if memx_err:
|
|
382
|
+
sdk_metrics["errors"]["get_memory"] = memx_err
|
|
383
|
+
sdk_metrics["get_memory"] = jsonable_value(memx)
|
|
384
|
+
|
|
385
|
+
# Output path
|
|
386
|
+
if args.json_out:
|
|
387
|
+
out_path = args.json_out
|
|
388
|
+
else:
|
|
389
|
+
out_path = default_json_out(args.qasm, backend_id, args.precision, int(args.threshold), int(args.shots), args.out_dir)
|
|
390
|
+
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
|
|
391
|
+
|
|
392
|
+
gpu_info = try_nvidia_smi()
|
|
393
|
+
|
|
394
|
+
payload: Dict[str, Any] = {
|
|
395
|
+
"meta": {
|
|
396
|
+
"created_utc": _dt.datetime.now(_dt.timezone.utc).replace(microsecond=0).isoformat(),
|
|
397
|
+
"original_qasm_file": os.path.basename(args.qasm),
|
|
398
|
+
"original_qasm_path": os.path.abspath(args.qasm),
|
|
399
|
+
"original_qasm_sha256": sha256_file(args.qasm),
|
|
400
|
+
"n_qubits": int(n_qubits),
|
|
401
|
+
"backend": str(args.backend),
|
|
402
|
+
"backend_id": backend_id,
|
|
403
|
+
"backend_repr": str(backend),
|
|
404
|
+
"precision": args.precision,
|
|
405
|
+
"threshold": int(args.threshold),
|
|
406
|
+
"shots": int(args.shots),
|
|
407
|
+
"mode": args.mirror_mode,
|
|
408
|
+
"sdk_pkg": sdk_pkg,
|
|
409
|
+
"sdk_version": sdk_ver,
|
|
410
|
+
},
|
|
411
|
+
"sdk": {
|
|
412
|
+
**run_kwargs
|
|
413
|
+
},
|
|
414
|
+
"system": {
|
|
415
|
+
"hostname": platform.node(),
|
|
416
|
+
"platform": platform.platform(),
|
|
417
|
+
"python": sys.version.split()[0],
|
|
418
|
+
"cpu_count": os.cpu_count(),
|
|
419
|
+
"gpu": gpu_info,
|
|
420
|
+
},
|
|
421
|
+
"timing_s": {
|
|
422
|
+
"run_wall_s": float(run_wall_s),
|
|
423
|
+
},
|
|
424
|
+
"memory": {
|
|
425
|
+
"peak_rss_bytes": int(peak_rss_bytes),
|
|
426
|
+
"peak_rss_mb": float(peak_rss_bytes) / (1024.0 * 1024.0),
|
|
427
|
+
},
|
|
428
|
+
"sdk_metrics": sdk_metrics,
|
|
429
|
+
"mirror_metrics": {
|
|
430
|
+
"shots_from_counts": int(shots_from_counts),
|
|
431
|
+
"p_return_zero": None if p_zero is None else float(p_zero),
|
|
432
|
+
"return_zero_bitstring": zero_key,
|
|
433
|
+
"unique_outcomes": int(len(items)),
|
|
434
|
+
"top": [{"bitstring": b, "count": int(c)} for b, c in top_items],
|
|
435
|
+
},
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
with open(out_path, "w", encoding="utf-8") as f:
|
|
439
|
+
json.dump(payload, f, indent=2, sort_keys=True)
|
|
440
|
+
|
|
441
|
+
print(f"[ok] wrote JSON: {out_path}", flush=True)
|
|
442
|
+
print(f"[mirror] p_return_zero={p_zero} unique_outcomes={len(items)}", flush=True)
|
|
443
|
+
print(f"[timing] run_wall_s={run_wall_s:.3f} [mem] peak_rss_mb={payload['memory']['peak_rss_mb']:.1f}", flush=True)
|
|
444
|
+
print(f"[sdk] get_fidelity={sdk_metrics.get('get_fidelity')} get_peakmemorysize_0={sdk_metrics.get('get_peakmemorysize_0')}", flush=True)
|
|
445
|
+
if sdk_metrics.get("errors"):
|
|
446
|
+
print(f"[sdk] errors={sdk_metrics['errors']}", flush=True)
|
|
447
|
+
print("===== done =====", flush=True)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
if __name__ == "__main__":
|
|
451
|
+
try:
|
|
452
|
+
main()
|
|
453
|
+
except SystemExit:
|
|
454
|
+
raise
|
|
455
|
+
except Exception as e:
|
|
456
|
+
import traceback
|
|
457
|
+
traceback.print_exc()
|
|
458
|
+
die(f"Unhandled exception: {e}")
|
|
459
|
+
|