-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_hardware.py
More file actions
153 lines (125 loc) · 5.42 KB
/
run_hardware.py
File metadata and controls
153 lines (125 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python
# SPDX-License-Identifier: AGPL-3.0-or-later | Commercial license available
# © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
# © Code 2020–2026 Miroslav Šotek. All rights reserved.
# ORCID: 0009-0009-3560-0851
# Contact: www.anulum.li | protoscience@anulum.li
# scpn-quantum-control — IBM Quantum Hardware CLI
"""CLI for running scpn-quantum-control experiments on IBM Quantum hardware.
Usage:
# One-time: save your IBM Quantum API token
python run_hardware.py --save-token YOUR_TOKEN
# Run all experiments on real hardware
python run_hardware.py --all
# Run specific experiment
python run_hardware.py --experiment kuramoto_4osc
# Run on local simulator (no token needed)
python run_hardware.py --simulator --experiment kuramoto_4osc
# Pick a specific backend
python run_hardware.py --backend ibm_torino --experiment vqe_4q
Available experiments:
kuramoto_4osc 4-oscillator Kuramoto XY dynamics (~30s QPU)
kuramoto_8osc 8-oscillator Kuramoto XY dynamics (~60s QPU)
vqe_4q VQE ground state, 4 qubits (~30s QPU)
vqe_8q VQE ground state, 8 qubits (~90s QPU)
qaoa_mpc_4 QAOA binary MPC, horizon=4 (~20s QPU)
upde_16_snapshot Full 16-layer UPDE snapshot (~180s QPU)
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
import numpy as np
def main():
parser = argparse.ArgumentParser(
description="Run scpn-quantum-control experiments on IBM Quantum hardware"
)
parser.add_argument("--save-token", metavar="TOKEN", help="Save IBM Quantum API token to disk")
parser.add_argument("--token", metavar="TOKEN", help="IBM Quantum API token (session only)")
parser.add_argument("--backend", metavar="NAME", help="Specific backend (default: least busy)")
parser.add_argument("--simulator", action="store_true", help="Use local AerSimulator")
parser.add_argument("--experiment", metavar="NAME", help="Run a specific experiment")
parser.add_argument("--all", action="store_true", help="Run all experiments")
parser.add_argument(
"--shots", type=int, default=10000, help="Shots per circuit (default: 10000)"
)
parser.add_argument(
"--results-dir", default="results", help="Output directory (default: results)"
)
parser.add_argument("--list", action="store_true", help="List available experiments")
args = parser.parse_args()
from scpn_quantum_control.hardware import ALL_EXPERIMENTS
from scpn_quantum_control.hardware.runner import HardwareRunner
if args.list:
print("Available experiments:")
for name in ALL_EXPERIMENTS:
print(f" {name}")
return
if args.save_token:
HardwareRunner.save_token(args.save_token)
return
runner = HardwareRunner(
token=args.token,
backend_name=args.backend,
use_simulator=args.simulator,
results_dir=args.results_dir,
)
runner.connect()
if args.experiment:
names = [args.experiment]
elif args.all:
names = list(ALL_EXPERIMENTS.keys())
else:
parser.print_help()
return
all_results = {}
for name in names:
if name not in ALL_EXPERIMENTS:
print(f"Unknown experiment: {name}")
print(f"Available: {', '.join(ALL_EXPERIMENTS.keys())}")
sys.exit(1)
fn = ALL_EXPERIMENTS[name]
result = fn(runner, shots=args.shots)
all_results[name] = result
_print_summary(name, result)
if len(all_results) > 1:
outpath = Path(args.results_dir) / "all_experiments.json"
with open(outpath, "w") as f:
json.dump(all_results, f, indent=2, default=_json_default)
print(f"\nAll results saved to {outpath}")
def _print_summary(name: str, result: dict):
print(f"\n{'-' * 60}")
print(f" {name} -- Summary")
print(f"{'-' * 60}")
if "hw_R" in result and "classical_R" in result:
if isinstance(result["hw_R"], list):
for i, (hr, cr) in enumerate(zip(result["hw_R"], result["classical_R"][1:])):
print(
f" t={result['hw_times'][i]:.2f} hw_R={hr:.4f} exact_R={cr:.4f} err={abs(hr - cr):.4f}"
)
else:
print(f" hw_R={result['hw_R']:.4f} exact_R={result['classical_R']:.4f}")
if "vqe_energy" in result:
print(f" VQE energy: {result['vqe_energy']:.6f}")
print(f" Exact energy: {result['exact_ground_energy']:.6f}")
print(f" Gap: {result['energy_gap']:.6f}")
print(f" Iterations: {result['n_iterations']}")
if "brute_force_cost" in result:
print(f" Brute-force cost: {result['brute_force_cost']:.6f}")
print(f" Brute-force action: {result['brute_force_actions']}")
for p in [1, 2]:
key = f"qaoa_p{p}"
if key in result:
print(f" QAOA p={p} cost: {result[key]['qaoa_cost']:.6f}")
print(f" QAOA p={p} action: {result[key]['qaoa_actions']}")
def _json_default(obj):
if isinstance(obj, np.ndarray):
return obj.tolist()
if isinstance(obj, (np.float64, np.float32)):
return float(obj)
if isinstance(obj, (np.int64, np.int32)):
return int(obj)
raise TypeError(f"Not JSON serializable: {type(obj)}")
if __name__ == "__main__":
main()