quafu-sdk 0.2.0__tar.gz
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.
- quafu_sdk-0.2.0/LICENSE +21 -0
- quafu_sdk-0.2.0/PKG-INFO +433 -0
- quafu_sdk-0.2.0/README.md +415 -0
- quafu_sdk-0.2.0/pyproject.toml +54 -0
- quafu_sdk-0.2.0/src/quafu/__init__.py +60 -0
- quafu_sdk-0.2.0/src/quafu/__main__.py +7 -0
- quafu_sdk-0.2.0/src/quafu/_conflict.py +121 -0
- quafu_sdk-0.2.0/src/quafu/_i18n.py +154 -0
- quafu_sdk-0.2.0/src/quafu/_inputs.py +92 -0
- quafu_sdk-0.2.0/src/quafu/_messages.py +423 -0
- quafu_sdk-0.2.0/src/quafu/_qasm.py +59 -0
- quafu_sdk-0.2.0/src/quafu/bits.py +93 -0
- quafu_sdk-0.2.0/src/quafu/cli.py +193 -0
- quafu_sdk-0.2.0/src/quafu/client.py +563 -0
- quafu_sdk-0.2.0/src/quafu/config.py +67 -0
- quafu_sdk-0.2.0/src/quafu/credentials.py +260 -0
- quafu_sdk-0.2.0/src/quafu/devices.py +91 -0
- quafu_sdk-0.2.0/src/quafu/errors.py +350 -0
- quafu_sdk-0.2.0/src/quafu/jobs.py +323 -0
- quafu_sdk-0.2.0/src/quafu/oauth.py +394 -0
- quafu_sdk-0.2.0/src/quafu/qiskit/__init__.py +225 -0
- quafu_sdk-0.2.0/src/quafu/tables.py +117 -0
- quafu_sdk-0.2.0/src/quafu/transport.py +99 -0
quafu_sdk-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Beijing Academy of Quantum Information Sciences (北京量子信息科学研究院)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
quafu_sdk-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quafu-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for the QUAFU quantum computing platform: log in, submit quantum circuits, and fetch results
|
|
5
|
+
Keywords: quantum,quantum-computing,quafu,sdk
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
12
|
+
Requires-Dist: qiskit>=1.3 ; extra == 'qiskit'
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Project-URL: Homepage, https://github.com/QUAFU-Quantum/quafu-tsingge
|
|
15
|
+
Project-URL: Issues, https://github.com/QUAFU-Quantum/quafu-tsingge/issues
|
|
16
|
+
Provides-Extra: qiskit
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# QUAFU SDK (Python)
|
|
20
|
+
|
|
21
|
+
[中文说明](https://github.com/QUAFU-Quantum/quafu-tsingge/blob/main/computing-interfaces/sdk/README.zh-CN.md)
|
|
22
|
+
|
|
23
|
+
Run quantum circuits on the real devices and simulators of the QUAFU quantum computing platform from Python, and fetch the results.
|
|
24
|
+
|
|
25
|
+
- Install: `pip install quafu-sdk`
|
|
26
|
+
- Import: `import quafu` (Qiskit backend adapter: `from quafu.qiskit import QuafuProvider`)
|
|
27
|
+
- Command line: `quafu login` / `quafu logout` / `quafu whoami` / `quafu config`
|
|
28
|
+
- Python 3.9 or later; no third-party runtime dependencies. To use Qiskit circuits as input, install `pip install "quafu-sdk[qiskit]"`.
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import quafu
|
|
34
|
+
|
|
35
|
+
bell = """OPENQASM 2.0;
|
|
36
|
+
include "qelib1.inc";
|
|
37
|
+
qreg q[2];
|
|
38
|
+
creg c[2];
|
|
39
|
+
h q[0];
|
|
40
|
+
cx q[0],q[1];
|
|
41
|
+
measure q[0] -> c[0];
|
|
42
|
+
measure q[1] -> c[1];
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
job = quafu.submit(bell, target="sim", shots=1024)
|
|
46
|
+
print(job.result().counts) # e.g. {'00': 517, '11': 507}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
On first run, the SDK prints a login link and tries to open your browser. Log in to QUAFU on the web page and click "Authorize"; the program then continues automatically.
|
|
50
|
+
|
|
51
|
+
## Log in
|
|
52
|
+
|
|
53
|
+
The SDK supports two ways to log in.
|
|
54
|
+
|
|
55
|
+
| | Browser login | API key |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| How to get it | `quafu.login()` or `quafu login`, then authorize in the browser | Create one on the website account page |
|
|
58
|
+
| Validity | 90 days from authorization; log in again when it expires (no renewal) | Never expires until disabled or deleted on the website |
|
|
59
|
+
| Best for | Personal computers, everyday use | Servers, scheduled jobs, CI, shared environments |
|
|
60
|
+
| Allowed operations | Submit, query, and cancel your own tasks | Same |
|
|
61
|
+
|
|
62
|
+
Neither method can manage your account or API keys; that can only be done on the website.
|
|
63
|
+
|
|
64
|
+
### Browser login
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
quafu.login() # returns immediately if already logged in with more than 7 days left
|
|
68
|
+
quafu.login(force=True) # log in again (switch account or renew early)
|
|
69
|
+
quafu.login(open_browser=False) # only print the link; don't open a browser
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
quafu login # same as quafu.login()
|
|
74
|
+
quafu login --force
|
|
75
|
+
quafu login --no-browser
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
During login the SDK waits for either of two completion paths; whichever finishes first wins:
|
|
79
|
+
|
|
80
|
+
- **Local browser**: open the link on this machine and authorize; the browser redirects back to the SDK automatically, nothing else to do.
|
|
81
|
+
- **Paste an authorization code**: in environments that can't redirect back to the local machine (SSH terminals, containers, remote Jupyter), the web page shows an authorization code after you authorize. Copy it, paste it into the terminal or notebook input box, and press Enter.
|
|
82
|
+
|
|
83
|
+
The wait lasts 10 minutes. When you are not logged in, the first call that needs a login (such as `submit()` or `whoami()`) starts a browser login automatically; in non-interactive environments (CI, background jobs, pipes) it raises `NotLoggedIn` right away instead of waiting for input.
|
|
84
|
+
|
|
85
|
+
Within 7 days of expiry, the SDK prints a reminder to standard error once per run. After expiry, run `quafu login` again; for long-running scripts, use an API key.
|
|
86
|
+
|
|
87
|
+
Example of a successful login:
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
Logged in as 甲 (u7f3a9c2b) browser login expires: 2027-01-06
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### API key
|
|
94
|
+
|
|
95
|
+
Create an API key (starting with `qf_`) on the website account page, then use any of the following:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
export QUAFU_API_KEY=qf_xxxxxxxx # environment variable; suited to servers and CI
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
c = quafu.Client(api_key="qf_xxxxxxxx") # pass explicitly; you can hold several Clients with different identities
|
|
103
|
+
quafu.login(api_key="qf_xxxxxxxx") # save on this machine (verified with the platform first; not saved if verification fails)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
echo "$KEY" | quafu login --api-key - # read from standard input on the command line and save
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Which credential is used
|
|
111
|
+
|
|
112
|
+
The first one found, in this order, is used; they are never merged:
|
|
113
|
+
|
|
114
|
+
1. Passed explicitly: `quafu.Client(api_key=...)`
|
|
115
|
+
2. Environment variable `QUAFU_API_KEY`
|
|
116
|
+
3. Saved login (from browser login or a key saved with `login(api_key=...)`)
|
|
117
|
+
4. None of the above: start a browser login if interactive, otherwise raise `NotLoggedIn`
|
|
118
|
+
|
|
119
|
+
`quafu.whoami()` or `quafu whoami` shows the current account, credential kind (`api_key` / `oauth`), source (`argument` / `env` / `saved`), and expiry time. Credentials are only shown masked (e.g. `qf_AbCdEfGh...`) and never appear in logs or error messages.
|
|
120
|
+
|
|
121
|
+
### Log out
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
quafu.logout()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
quafu logout
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
For a browser login, the SDK first revokes the login on the platform, then deletes the saved login. The saved login is deleted even if the revoke request fails; in that case, revoke it under "Signed-in devices" on the website. If the saved credential is an API key, only the local copy is deleted and the key itself stays valid; to invalidate it, disable or delete it on the website account page.
|
|
132
|
+
|
|
133
|
+
### Saved location
|
|
134
|
+
|
|
135
|
+
The saved login is stored in `~/.quafu-sdk/credentials.json` (directory mode 0700, file mode 0600). Set the environment variable `QUAFU_CONFIG_DIR` to use another directory. One login is saved per platform URL.
|
|
136
|
+
|
|
137
|
+
**Shared environments**: every program running under the same system account can read this file. On servers or Jupyter kernels where several people share an account, use the `QUAFU_API_KEY` environment variable instead, and disable the key on the website when you are done.
|
|
138
|
+
|
|
139
|
+
## Display language
|
|
140
|
+
|
|
141
|
+
CLI messages, login prompts, and exception messages are in English by default. Chinese is also available. The SDK never switches language based on the system locale.
|
|
142
|
+
|
|
143
|
+
Ways to set it, highest precedence first:
|
|
144
|
+
|
|
145
|
+
1. In Python: `quafu.set_language('zh')`. Applies to the current process only and is not saved; `quafu.set_language(None)` clears it. `quafu.get_language()` returns the effective language.
|
|
146
|
+
2. Environment variable `QUAFU_LANG=zh` (or `en`). Forms such as `zh_CN`, `zh-CN`, `en_US`, and `zh_CN.UTF-8` are also accepted. Unrecognized values are ignored.
|
|
147
|
+
3. Saved setting: `quafu config set language zh` / `quafu config get language`. Stored in `~/.quafu-sdk/config.json` (same directory as the credentials, honors `QUAFU_CONFIG_DIR`), separate from credentials and shared across platform URLs.
|
|
148
|
+
4. Default: `en`.
|
|
149
|
+
|
|
150
|
+
**First login**: the browser-login prompt ends with a line in the other language. In English mode it is `使用中文请按 Z 并回车。`; in Chinese mode it is `Press E then Enter to set English as display language.` Typing just `Z` (or `E`), case-insensitive, and pressing Enter switches the language, saves it the same way as `quafu config set language`, reprints the prompt in the new language, and keeps waiting. On Windows a single keypress is enough when nothing else has been typed. Authorization codes are never a single letter, so this doesn't conflict with pasting a code. The hint line appears only while no language has been chosen (no environment variable, no saved setting) and only in interactive sessions; the keys work in any interactive login.
|
|
151
|
+
|
|
152
|
+
## Submit circuits
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
job = quafu.submit(
|
|
156
|
+
circuits, # OpenQASM 2.0 text or qiskit.QuantumCircuit, or a list of them
|
|
157
|
+
target="sim", # where to run; see the table below
|
|
158
|
+
shots=1024, # number of runs per circuit
|
|
159
|
+
compiler="qiskit", # compilation mode; see the table below
|
|
160
|
+
name=None, # optional job name, shown under "My jobs" on the website
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Each `submit()` creates one **job**; each circuit in the list is one **task** in the job, in submission order. `submit()` returns immediately without waiting for the run to finish.
|
|
165
|
+
|
|
166
|
+
**target**
|
|
167
|
+
|
|
168
|
+
| Value | Meaning |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `"sim"` | Ideal simulator |
|
|
171
|
+
| `"<device>"`, e.g. `"Dongling"` | The named real device; never moved to another device |
|
|
172
|
+
| `"<device>-sim"`, e.g. `"Dongling-sim"` | Noisy simulation based on that device's calibration data |
|
|
173
|
+
| `"all-race"` | Send to all online real devices at once and take the first result to finish |
|
|
174
|
+
| `"all-redispatch"` | Send to one real device first, automatically moving to the next if it is unavailable |
|
|
175
|
+
|
|
176
|
+
Query available names and their current status with `quafu.devices()`:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
for d in quafu.devices():
|
|
180
|
+
print(d["name"], d["kind"], d["status"], d["n_qubits"], d["queue"])
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`kind` is `real` (real device) or `sim` (simulator); `queue` is the number of tasks queued ahead on a real device, or `None` if unknown. Querying devices does not require login.
|
|
184
|
+
|
|
185
|
+
**shots**: real devices require a multiple of 1024 (1024–8192). The platform checks this and raises `PrecheckError` if it is not met.
|
|
186
|
+
|
|
187
|
+
**compiler**
|
|
188
|
+
|
|
189
|
+
| Value | Meaning |
|
|
190
|
+
|---|---|
|
|
191
|
+
| `"qiskit"` (default) | The platform compiles the circuit for the target chip. Write a logical circuit and declare only the qubits you use |
|
|
192
|
+
| `"none"` | No compilation; runs as is. The circuit must already be a physical circuit for this chip, with qubit indices matching the chip's numbering |
|
|
193
|
+
|
|
194
|
+
The compiler and version actually used are echoed in the result's `compiler` field.
|
|
195
|
+
|
|
196
|
+
**Qiskit circuits**
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from qiskit import QuantumCircuit
|
|
200
|
+
|
|
201
|
+
qc = QuantumCircuit(2, 2)
|
|
202
|
+
qc.h(0)
|
|
203
|
+
qc.cx(0, 1)
|
|
204
|
+
qc.measure([0, 1], [0, 1])
|
|
205
|
+
job = quafu.submit(qc, target="sim")
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The SDK uses Qiskit to export the circuit to OpenQASM 2.0 before submitting, and sorts the trailing measurement statements by classical bit in ascending order (required by real devices). Circuits with unbound parameters must be bound with `assign_parameters` before submission.
|
|
209
|
+
|
|
210
|
+
## Fetch results
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
job.id # job ID; after the program exits, reattach with quafu.job(job.id)
|
|
214
|
+
job.status() # Queued / Running / Finished / PartiallyFinished / Failed / Cancelled
|
|
215
|
+
r = job.result(timeout=600) # single circuit: wait until done, return a Result
|
|
216
|
+
rs = job.results() # multiple circuits: return a list of Results in submission order
|
|
217
|
+
job.cancel() # cancel tasks that have not finished
|
|
218
|
+
job = quafu.job("u7f3a9c2b-261008-153000") # reattach by job ID (only your own jobs)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
- `result()` is only for jobs with a single circuit; it raises `JobFailed` on failure and `JobCancelled` if cancelled.
|
|
222
|
+
- `results()` does not raise when individual circuits fail: failed ones have `counts` set to `None`, and `status` and `error` explain why.
|
|
223
|
+
- If the job doesn't finish within `timeout` seconds, `Timeout` is raised; the job remains on the platform, and you can call again to keep waiting. Ctrl-C only stops waiting; it does not cancel the job.
|
|
224
|
+
- `cancel()` returns the platform's response: `cancelled_now` is the number of tasks cancelled outright; `cancel_requested` is the number already sent to a real device, for which waiting can only be stopped (quota already consumed is not refunded). Tasks whose results are being fetched are not cancelled and produce results as usual.
|
|
225
|
+
|
|
226
|
+
**Result**
|
|
227
|
+
|
|
228
|
+
| Field | Meaning |
|
|
229
|
+
|---|---|
|
|
230
|
+
| `counts` | Raw counts `{bitstring: count}`; `None` if unsuccessful |
|
|
231
|
+
| `bit_order` | Bit order; `"c0_rightmost"` means the rightmost character of the bitstring is classical bit `c[0]` |
|
|
232
|
+
| `n_clbits` | Number of classical bits |
|
|
233
|
+
| `shots_requested` / `shots_returned` | Shots requested / shots actually counted (normalize with the latter) |
|
|
234
|
+
| `probabilities()` | Probabilities normalized by `shots_returned` |
|
|
235
|
+
| `measurements` | `[{cbit, physical_qubit, virtual_qubit}]`: which physical qubit each classical bit measured |
|
|
236
|
+
| `counts_by_qubit([…])` / `reorder(…)` / `expectation_z(…)` | Counts rearranged by physical qubit or classical bit, Z-string expectation values; see "Per-qubit data" |
|
|
237
|
+
| `executed_qasm` | The circuit actually run |
|
|
238
|
+
| `compiler` | Compiler used: `{name, version, optimization_level, seed}` |
|
|
239
|
+
| `calibration_id` | ID of the calibration data used for this run (`None` for ideal simulation) |
|
|
240
|
+
| `target` / `backend` | Submitted target / backend actually used (for `all-race` and similar, the real device it landed on) |
|
|
241
|
+
| `status` / `ok` / `error` | Status of this task; failure reason `{category, stage, retryable, detail, vars}` |
|
|
242
|
+
| `parameter_values` | Parameter values for this task (sweeps, iterations, parameterized circuits); `None` if there are none |
|
|
243
|
+
| `iteration` / `evaluation_index` | Round and evaluation index in an iterative job |
|
|
244
|
+
| `task_id` / `raw` | Task ID; full details returned by the platform |
|
|
245
|
+
|
|
246
|
+
## Parameter sweeps
|
|
247
|
+
|
|
248
|
+
In a circuit template, write gate parameters as `{{param_name}}`, then provide a set of parameter points. The platform substitutes each point in turn; each point is one task in the job:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
template = """OPENQASM 2.0;
|
|
252
|
+
include "qelib1.inc";
|
|
253
|
+
qreg q[1];
|
|
254
|
+
creg c[1];
|
|
255
|
+
rx({{theta}}) q[0];
|
|
256
|
+
measure q[0] -> c[0];
|
|
257
|
+
"""
|
|
258
|
+
job = quafu.sweep(template, parameters=[("theta", "rad")], points=[0.0, 0.5, 1.0], target="sim")
|
|
259
|
+
t = job.table()
|
|
260
|
+
t.column("theta") # [0.0, 0.5, 1.0]
|
|
261
|
+
t.expectation_z([0]) # <Z> at each point
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- `parameters`: name and unit of each parameter (`"rad"`, `"ns"`, etc.; `"1"` for dimensionless). The order defines the column order of each row in `points`.
|
|
265
|
+
- `points`: table of parameter points, as a nested list or numpy array; with a single parameter, each row can be a single number.
|
|
266
|
+
- Other arguments (`shots`, `compiler`, `name`) are the same as for `submit()`.
|
|
267
|
+
|
|
268
|
+
You can also substitute locally and submit a list of circuits, labeling each with its parameter values: `quafu.submit([{"qasm": ..., "parameter_values": {"theta": 0.5}}, ...], target, parameters=[("theta", "rad")])`. For Qiskit parameterized circuits, see "Qiskit" below.
|
|
269
|
+
|
|
270
|
+
## Iterative jobs
|
|
271
|
+
|
|
272
|
+
Variational algorithms (VQE, QAOA, etc.) need to "look at this round's results before deciding the next round". Open an iterative job and append round by round:
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
job = quafu.open_job("sim", template=template, parameters=[("theta", "rad")], name="VQE")
|
|
276
|
+
theta = 0.1
|
|
277
|
+
for k in range(10):
|
|
278
|
+
job.append(points=[[theta]], iteration=k)
|
|
279
|
+
job.wait() # returns when no tasks are queued or running
|
|
280
|
+
z = job.results()[-1].expectation_z([0])
|
|
281
|
+
theta = theta - 0.1 * z # illustration: update the parameter from the result
|
|
282
|
+
job.close() # close when done; no more appends afterwards
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
- `append(points=...)` substitutes into the template (the job must be opened with `template` and `parameters`); `append(circuits=[...])` appends circuits directly (text, Qiskit circuits, or circuit items with `parameter_values`).
|
|
286
|
+
- `iteration` is the round number and `evaluation_index` the evaluation number within the round; both appear in the results.
|
|
287
|
+
- Append requests carry an idempotency key, so automatic retries after a lost response never append twice. Appending to a closed job raises `JobClosed`.
|
|
288
|
+
|
|
289
|
+
## Result tables
|
|
290
|
+
|
|
291
|
+
`job.table()` returns a columnar table (`ResultTable`) with rows aligned in submission order, suited to sweeps and iterations:
|
|
292
|
+
|
|
293
|
+
| Column | Meaning |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `seq` / `task_ids` | Sequence number / task ID |
|
|
296
|
+
| `points`, `column("param_name")` | Parameter values of each row |
|
|
297
|
+
| `status` / `counts` / `shots_returned` | Status, counts, and shots actually counted per row; `counts` is `None` for unsuccessful rows |
|
|
298
|
+
| `iterations` / `evaluation_index` | Round and evaluation index in an iterative job |
|
|
299
|
+
| `errors` | Reasons for unsuccessful rows |
|
|
300
|
+
|
|
301
|
+
`t.probabilities()`, `t.reordered(order)`, and `t.expectation_z(cbits)` compute row by row; `t.to_dict()` or `t.to_pandas()` (requires pandas) converts to a table. `job.table(wait=False)` returns the current snapshot immediately, useful for inspecting progress during iterations.
|
|
302
|
+
|
|
303
|
+
## Per-qubit data
|
|
304
|
+
|
|
305
|
+
Both `Result` and `ResultTable` carry a bit-order declaration (`bit_order`) and a measurement mapping (`measurements`), so conversions are direct:
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
r.reorder([1, 0]) # bitstring reads c[1], c[0] from left to right; listing only some bits gives a marginal distribution
|
|
309
|
+
r.reorder("c0_leftmost") # switch to c[0] on the far left
|
|
310
|
+
r.counts_by_qubit([108, 109])# select by physical qubit
|
|
311
|
+
r.expectation_z([0, 1]) # Z⊗Z expectation value (computed from raw counts only)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Functions of the same names are also available at the top level: `quafu.reorder_counts`, `quafu.counts_by_qubit`, `quafu.probabilities`, `quafu.expectation_z`.
|
|
315
|
+
|
|
316
|
+
## Devices and calibration data
|
|
317
|
+
|
|
318
|
+
```python
|
|
319
|
+
d = quafu.device("Dongling") # status, qubit count, queue length, current calibration ID
|
|
320
|
+
cal = d.calibration() # current calibration data
|
|
321
|
+
cal.qubit(5)["t1_us"] # T1 of qubit 5 (microseconds)
|
|
322
|
+
cal.coupler(5, 6)["cz_fidelity"] # CZ gate fidelity between qubits 5 and 6
|
|
323
|
+
cal.measured_qubit(5) # readout data measured by the platform
|
|
324
|
+
quafu.calibration(r.calibration_id) # the calibration data used by a given run (immutable, retrievable long-term)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Field names include units (e.g. `t1_us`); fidelities are decimals from 0 to 1; values not measured or not filled in are `None`. Querying devices and calibration data does not require login.
|
|
328
|
+
|
|
329
|
+
## Usage statistics
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
quafu.stats(days=30) # your task count, shots, daily breakdown, today's quota and runtime over the last 30 days
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Qiskit
|
|
336
|
+
|
|
337
|
+
```python
|
|
338
|
+
from qiskit import QuantumCircuit, transpile
|
|
339
|
+
from quafu.qiskit import QuafuProvider, sweep
|
|
340
|
+
|
|
341
|
+
backend = QuafuProvider().get_backend("sim") # "sim", "<device>-sim", or "<device>"
|
|
342
|
+
job = backend.run(transpile(qc, backend), shots=1024)
|
|
343
|
+
job.result().get_counts()
|
|
344
|
+
|
|
345
|
+
job = sweep(qc_with_parameters, points, "sim") # parameterized circuit: substituted point by point locally, submitted as one job
|
|
346
|
+
job.table().column("theta")
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
- The backend's gate set and topology come from the platform; `backend.run()` submits all circuits as one job. When some circuits fail, the failed experiments have `success=False`.
|
|
350
|
+
- `sweep()` keeps the parameter names from Qiskit; the unit defaults to `"rad"`.
|
|
351
|
+
- Requires `pip install "quafu-sdk[qiskit]"`.
|
|
352
|
+
|
|
353
|
+
## Platform URL
|
|
354
|
+
|
|
355
|
+
By default the SDK connects to `https://quafu.com.cn`. To connect to another deployment:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
c = quafu.Client(base_url="https://example.org")
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
export QUAFU_BASE_URL=https://example.org
|
|
363
|
+
quafu --base-url https://example.org whoami
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Precedence: argument > environment variable `QUAFU_BASE_URL` > default URL. Give the host only (no path), and it must be https, except for local loopback addresses (`http://127.0.0.1:<port>`).
|
|
367
|
+
|
|
368
|
+
## Network
|
|
369
|
+
|
|
370
|
+
- Certificates are always verified; there is no option to disable verification. For deployments with self-signed certificates, specify the CA certificate with `Client(ca_bundle="ca.pem")` or the environment variable `QUAFU_CA_BUNDLE`.
|
|
371
|
+
- System proxy settings are not used by default. To use a proxy, specify an HTTP proxy with `Client(proxy="host:port")` or the environment variable `QUAFU_PROXY`.
|
|
372
|
+
- Read requests are retried automatically on network errors or platform 5xx responses; submissions carry an idempotency key, so retries after a lost response never submit twice.
|
|
373
|
+
|
|
374
|
+
## Errors
|
|
375
|
+
|
|
376
|
+
All exceptions inherit from `quafu.QuafuError`; error messages explain the cause and what to do.
|
|
377
|
+
|
|
378
|
+
| Exception | When it occurs |
|
|
379
|
+
|---|---|
|
|
380
|
+
| `NotLoggedIn` | No credentials, and the environment is not interactive |
|
|
381
|
+
| `LoginFailed` | Browser login did not complete: authorization denied, timed out, or invalid authorization code |
|
|
382
|
+
| `LoginExpired` | The saved browser login has expired |
|
|
383
|
+
| `LoginRevoked` | The platform rejects the credential: the login was revoked, or the API key was disabled or deleted |
|
|
384
|
+
| `PrecheckError` | The platform rejected the circuit or parameters; `category` and `vars` give the specific reason |
|
|
385
|
+
| `QuotaError` | Quota or rate limit exceeded; `retry_after` is the suggested wait in seconds |
|
|
386
|
+
| `JobFailed` / `JobCancelled` | Circuit run failed / was cancelled; `retryable` indicates whether resubmitting unchanged makes sense |
|
|
387
|
+
| `JobNotFound` | The job does not exist or does not belong to the current account |
|
|
388
|
+
| `JobClosed` | Appending tasks to a closed or non-iterative job |
|
|
389
|
+
| `Timeout` | Waiting exceeded `timeout` |
|
|
390
|
+
| `ServerUnavailable` | Cannot connect to the platform, or the platform is temporarily unavailable |
|
|
391
|
+
|
|
392
|
+
## FAQ
|
|
393
|
+
|
|
394
|
+
**`import quafu` reports "another package occupying the quafu name", or `quafu.login` is missing after import**
|
|
395
|
+
|
|
396
|
+
The current Python environment has another package installed that also uses the name `quafu`, and the two overwrite each other's files. Create a clean virtual environment with [uv](https://docs.astral.sh/uv/) and install again. The error message shows only the commands for your system; each block below can be pasted as-is.
|
|
397
|
+
|
|
398
|
+
macOS / Linux:
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
uv venv quafu-env
|
|
402
|
+
source quafu-env/bin/activate
|
|
403
|
+
uv pip install quafu-sdk
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Windows PowerShell (the second line lets this window run `Activate.ps1`):
|
|
407
|
+
|
|
408
|
+
```powershell
|
|
409
|
+
uv venv quafu-env
|
|
410
|
+
Set-ExecutionPolicy Bypass -Scope Process -Force
|
|
411
|
+
quafu-env\Scripts\Activate.ps1
|
|
412
|
+
uv pip install quafu-sdk
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Windows cmd:
|
|
416
|
+
|
|
417
|
+
```bat
|
|
418
|
+
uv venv quafu-env
|
|
419
|
+
quafu-env\Scripts\activate.bat
|
|
420
|
+
uv pip install quafu-sdk
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**`NotLoggedIn` when running on a server or in CI**
|
|
424
|
+
|
|
425
|
+
These environments are not interactive, so no browser login is started. Create an API key on the website account page and set the environment variable `QUAFU_API_KEY`.
|
|
426
|
+
|
|
427
|
+
**The browser doesn't redirect back after browser login**
|
|
428
|
+
|
|
429
|
+
This is expected in remote terminals, containers, or remote Jupyter. Copy the authorization code shown on the web page, paste it into the terminal or notebook input box, and press Enter.
|
|
430
|
+
|
|
431
|
+
## License
|
|
432
|
+
|
|
433
|
+
MIT
|