qtwist 5.0.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.
- qtwist-5.0.0/LICENSE +15 -0
- qtwist-5.0.0/MANIFEST.in +15 -0
- qtwist-5.0.0/PKG-INFO +163 -0
- qtwist-5.0.0/README.md +129 -0
- qtwist-5.0.0/pyproject.toml +51 -0
- qtwist-5.0.0/qtwist/__init__.py +11 -0
- qtwist-5.0.0/qtwist/__main__.py +5 -0
- qtwist-5.0.0/qtwist/aer_compat.py +265 -0
- qtwist-5.0.0/qtwist/api.py +2303 -0
- qtwist-5.0.0/qtwist/cli.py +517 -0
- qtwist-5.0.0/qtwist/payments.py +392 -0
- qtwist-5.0.0/qtwist/pricing.py +192 -0
- qtwist-5.0.0/qtwist/qpu_backend.py +437 -0
- qtwist-5.0.0/qtwist/qsim.py +14 -0
- qtwist-5.0.0/qtwist/router.py +211 -0
- qtwist-5.0.0/qtwist/web/__init__.py +1 -0
- qtwist-5.0.0/qtwist/web/api.py +589 -0
- qtwist-5.0.0/qtwist/web/app.py +537 -0
- qtwist-5.0.0/qtwist/web/static/app.js +759 -0
- qtwist-5.0.0/qtwist/web/static/index.html +51 -0
- qtwist-5.0.0/qtwist/web/static/styles.css +275 -0
- qtwist-5.0.0/qtwist/web/store.py +408 -0
- qtwist-5.0.0/qtwist/zne.py +76 -0
- qtwist-5.0.0/qtwist.egg-info/PKG-INFO +163 -0
- qtwist-5.0.0/qtwist.egg-info/SOURCES.txt +78 -0
- qtwist-5.0.0/qtwist.egg-info/dependency_links.txt +1 -0
- qtwist-5.0.0/qtwist.egg-info/entry_points.txt +2 -0
- qtwist-5.0.0/qtwist.egg-info/requires.txt +25 -0
- qtwist-5.0.0/qtwist.egg-info/top_level.txt +2 -0
- qtwist-5.0.0/qtwist_core/__init__.py +17 -0
- qtwist-5.0.0/qtwist_core/certification/__init__.py +15 -0
- qtwist-5.0.0/qtwist_core/certification/consistency_checker.py +130 -0
- qtwist-5.0.0/qtwist_core/certification/cross_validator.py +147 -0
- qtwist-5.0.0/qtwist_core/certification/error_budget.py +675 -0
- qtwist-5.0.0/qtwist_core/certification/generalization.py +348 -0
- qtwist-5.0.0/qtwist_core/certification/knn_baseline.py +86 -0
- qtwist-5.0.0/qtwist_core/certification/models/mlp_mitigator.pt +0 -0
- qtwist-5.0.0/qtwist_core/certification/models/mlp_mitigator_scalable.pt +0 -0
- qtwist-5.0.0/qtwist_core/certification/models/mlp_mitigator_scalable_scaler.json +1 -0
- qtwist-5.0.0/qtwist_core/certification/models/mlp_mitigator_scaler.json +1 -0
- qtwist-5.0.0/qtwist_core/certification/models/xgb_gap_predictor.json +1 -0
- qtwist-5.0.0/qtwist_core/certification/models/xgb_gap_predictor_scalable.json +1 -0
- qtwist-5.0.0/qtwist_core/certification/neural_features.py +371 -0
- qtwist-5.0.0/qtwist_core/certification/noise_oracle.py +191 -0
- qtwist-5.0.0/qtwist_core/certification/reference_circuits.py +152 -0
- qtwist-5.0.0/qtwist_core/core/__init__.py +20 -0
- qtwist-5.0.0/qtwist_core/core/circuit.py +333 -0
- qtwist-5.0.0/qtwist_core/core/engine_a.py +274 -0
- qtwist-5.0.0/qtwist_core/core/engine_b.py +337 -0
- qtwist-5.0.0/qtwist_core/core/engine_b_mpdo.py +238 -0
- qtwist-5.0.0/qtwist_core/core/engine_c.py +901 -0
- qtwist-5.0.0/qtwist_core/core/engine_d_adaptive.py +1610 -0
- qtwist-5.0.0/qtwist_core/core/engine_f_tnt.py +610 -0
- qtwist-5.0.0/qtwist_core/core/gate_conventions.py +471 -0
- qtwist-5.0.0/qtwist_core/core/zne.py +562 -0
- qtwist-5.0.0/qtwist_core/physics/__init__.py +17 -0
- qtwist-5.0.0/qtwist_core/physics/calibration_ingest.py +235 -0
- qtwist-5.0.0/qtwist_core/physics/consistency_checker_200q.py +291 -0
- qtwist-5.0.0/qtwist_core/physics/drift_tracker.py +142 -0
- qtwist-5.0.0/qtwist_core/physics/hardware_interface.py +617 -0
- qtwist-5.0.0/qtwist_core/physics/hardware_validation.py +585 -0
- qtwist-5.0.0/qtwist_core/physics/lindbladian.py +499 -0
- qtwist-5.0.0/qtwist_core/physics/mock_qpu.py +73 -0
- qtwist-5.0.0/qtwist_core/physics/mock_qpu_200q.py +200 -0
- qtwist-5.0.0/qtwist_core/physics/snapshot.py +91 -0
- qtwist-5.0.0/qtwist_core/physics/spl_noise_model.py +288 -0
- qtwist-5.0.0/qtwist_core/py.typed +0 -0
- qtwist-5.0.0/qtwist_core/router/__init__.py +11 -0
- qtwist-5.0.0/qtwist_core/router/dispatcher.py +1053 -0
- qtwist-5.0.0/qtwist_core/router/lightcone.py +72 -0
- qtwist-5.0.0/qtwist_core/router/snake_mapper.py +172 -0
- qtwist-5.0.0/qtwist_core/tests/test_cross_engine.py +1203 -0
- qtwist-5.0.0/qtwist_core/utils/__init__.py +9 -0
- qtwist-5.0.0/qtwist_core/utils/bitstring_mapper.py +144 -0
- qtwist-5.0.0/qtwist_core/utils/transpiler.py +625 -0
- qtwist-5.0.0/setup.cfg +4 -0
- qtwist-5.0.0/tests/conftest.py +11 -0
- qtwist-5.0.0/tests/test_cli.py +37 -0
- qtwist-5.0.0/tests/test_engine.py +38 -0
- qtwist-5.0.0/tests/test_web.py +99 -0
qtwist-5.0.0/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Q-TWIST — PROPRIETARY SOFTWARE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Q-TWIST. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software is the proprietary property of Q-TWIST. It is licensed for
|
|
6
|
+
use only under the terms of a separate written license agreement with
|
|
7
|
+
Q-TWIST. No rights are granted except as expressly set forth in such an
|
|
8
|
+
agreement. Unauthorized reproduction, distribution, or use is prohibited.
|
|
9
|
+
|
|
10
|
+
The Q-TWIST Python SDK (`pip install qtwist`) and web console are provided
|
|
11
|
+
"AS IS" without warranty of any kind, express or implied, including but not
|
|
12
|
+
limited to the warranties of merchantability, fitness for a particular
|
|
13
|
+
purpose, and non-infringement.
|
|
14
|
+
|
|
15
|
+
For licensing inquiries, contact Q-TWIST.
|
qtwist-5.0.0/MANIFEST.in
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
include pyproject.toml
|
|
2
|
+
include README.md
|
|
3
|
+
include LICENSE
|
|
4
|
+
|
|
5
|
+
recursive-include qtwist *.py
|
|
6
|
+
recursive-include qtwist/web *.py *.html *.css *.js
|
|
7
|
+
recursive-include qtwist_core *.py *.pt *.json py.typed
|
|
8
|
+
recursive-include tests *.py
|
|
9
|
+
|
|
10
|
+
prune build
|
|
11
|
+
prune dist
|
|
12
|
+
prune .venv
|
|
13
|
+
prune research
|
|
14
|
+
|
|
15
|
+
global-exclude *.pyc __pycache__
|
qtwist-5.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qtwist
|
|
3
|
+
Version: 5.0.0
|
|
4
|
+
Summary: Q-TWIST: certified-methodology digital twin for noisy quantum processors — engines, Python SDK, and web console.
|
|
5
|
+
Author: Q-TWIST
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://qtwist.example
|
|
8
|
+
Keywords: quantum,simulation,certified,digital-twin,noise
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: numpy>=2.0
|
|
13
|
+
Requires-Dist: scipy>=1.11
|
|
14
|
+
Requires-Dist: threadpoolctl>=3.0
|
|
15
|
+
Requires-Dist: qiskit>=1.0
|
|
16
|
+
Requires-Dist: qiskit-aer>=0.14
|
|
17
|
+
Requires-Dist: qiskit-ibm-runtime>=0.40
|
|
18
|
+
Requires-Dist: quimb>=1.8
|
|
19
|
+
Requires-Dist: fastapi>=0.110
|
|
20
|
+
Requires-Dist: uvicorn[standard]>=0.27
|
|
21
|
+
Requires-Dist: pydantic>=2.0
|
|
22
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
23
|
+
Provides-Extra: torch
|
|
24
|
+
Requires-Dist: torch>=2.0; extra == "torch"
|
|
25
|
+
Provides-Extra: billing
|
|
26
|
+
Requires-Dist: razorpay>=2.0; extra == "billing"
|
|
27
|
+
Provides-Extra: full
|
|
28
|
+
Requires-Dist: torch>=2.0; extra == "full"
|
|
29
|
+
Requires-Dist: razorpay>=2.0; extra == "full"
|
|
30
|
+
Provides-Extra: test
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
32
|
+
Requires-Dist: httpx>=0.27; extra == "test"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# Q-TWIST v5.0 (Beta) — The Honest Oracle
|
|
36
|
+
|
|
37
|
+
Q-TWIST is a physics-constrained quantum-circuit inference engine that tells
|
|
38
|
+
you **what it can simulate, to what error, and whether your circuit is worth
|
|
39
|
+
running on real hardware** — and refuses, honestly, when it cannot. No silent
|
|
40
|
+
wrong numbers. No "certified digital twin" marketing. Rigorous L1 error bounds,
|
|
41
|
+
and an explicit refusal with a fix when a circuit is out of envelope.
|
|
42
|
+
|
|
43
|
+
It ships in two tiers:
|
|
44
|
+
|
|
45
|
+
* **FREE** — a local **`AerSimulator`-style drop-in** that runs on your laptop
|
|
46
|
+
CPU. Drop it into any Qiskit expectation-value workflow. No token, no bill.
|
|
47
|
+
* **PRO** — a **metered cloud GPU instance** unlocked by a premium token.
|
|
48
|
+
Costs are previewed *before* every run (IBM-style credit wallet). Adds the
|
|
49
|
+
**QPU-Readiness Oracle**: a plain-English verdict on whether your circuit is
|
|
50
|
+
good to submit to a real QPU.
|
|
51
|
+
|
|
52
|
+
> Live QPU routing is **roadmap**, not shipped. Everything today is classical
|
|
53
|
+
> simulation + an honest readiness verdict. See `qpu_backend.py` for the scaffold.
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
python -m pip install qtwist
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
(Optional full-stack extras for Qiskit circuit input and the adaptive-MPS
|
|
62
|
+
engine: `python -m pip install -e ".[full]"`.)
|
|
63
|
+
|
|
64
|
+
## Quickstart — free local drop-in
|
|
65
|
+
|
|
66
|
+
You only need standard Qiskit. `QtwistSimulator` mirrors `AerSimulator`'s
|
|
67
|
+
`run()` / `result()` surface for the expectation-value workflow:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from qiskit import QuantumCircuit
|
|
71
|
+
from qtwist.aer_compat import QtwistSimulator
|
|
72
|
+
|
|
73
|
+
# 20-qubit transverse-field Ising model, 3 layers
|
|
74
|
+
qc = QuantumCircuit(20)
|
|
75
|
+
for _ in range(3):
|
|
76
|
+
for i in range(19):
|
|
77
|
+
qc.rzz(0.5, i, i + 1)
|
|
78
|
+
for i in range(20):
|
|
79
|
+
qc.rx(0.3, i)
|
|
80
|
+
|
|
81
|
+
sim = QtwistSimulator(instance="local") # free, laptop CPU
|
|
82
|
+
res = sim.run(qc, observable="Z0Z1", epsilon_target=0.05).result()
|
|
83
|
+
|
|
84
|
+
print(f"<Z0 Z1> = {res.value():+.6f} +/- {res.error:.4f}")
|
|
85
|
+
print(res.explain()) # QPU-Readiness Oracle verdict
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For small circuits (n ≤ 18) on the laptop you can also sample counts:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
counts = res.get_counts(shots=1024) # Qiskit bit-order (LSB-first)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## The QPU-Readiness Oracle
|
|
95
|
+
|
|
96
|
+
Every result carries a `qpu_readiness` verdict — the one thing you want to
|
|
97
|
+
know before spending real QPU queue time:
|
|
98
|
+
|
|
99
|
+
| Verdict | Meaning | What to do |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `READY_FOR_QPU` | Within Q-TWIST's validated envelope; value trustworthy within the reported L1 bound | Run it on hardware to validate the physics |
|
|
102
|
+
| `NEEDS_FIXES` | Rejected only for a fixable issue (long-range gate, multi-controlled gate, wrong topology) | Apply the listed suggestions, re-run |
|
|
103
|
+
| `INTRACTABLE` | Beyond classical simulation / QPU-advantage territory | Run on hardware directly if you must; Q-TWIST can't pre-validate |
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
print(res.explain())
|
|
107
|
+
# [QPU-Readiness: READY_FOR_QPU]
|
|
108
|
+
# Circuit is within Q-TWIST's validated envelope. The simulated value ...
|
|
109
|
+
# value=+0.993923 L1 bound=0.1287 cert=UNCERTIFIED (no live QPU validation)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Quickstart — premium cloud tier (metered)
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from qtwist.aer_compat import QtwistSimulator
|
|
116
|
+
from qtwist.pricing import Wallet
|
|
117
|
+
|
|
118
|
+
wallet = Wallet(token="PRO_TOKEN") # $99 base = 100 credits
|
|
119
|
+
sim = QtwistSimulator(instance="cloud:PRO_TOKEN", wallet=wallet, topology="1D_chain")
|
|
120
|
+
|
|
121
|
+
print(sim.estimate(qc)) # pre-flight cost, never bills
|
|
122
|
+
# [PRO / cloud] engine=ENGINE_D n=20 depth=39 -- est. 0.97 credits (~$0.96) ...
|
|
123
|
+
|
|
124
|
+
res = sim.run(qc, observable="Z0Z1").result() # charged only after confirm
|
|
125
|
+
print(res.explain())
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Tiers:
|
|
129
|
+
| Tier | Runs on | Billing | Capability Classifier | QPU-Readiness Oracle |
|
|
130
|
+
|---|---|---|---|---|
|
|
131
|
+
| FREE | laptop CPU | free | instant | yes (local) |
|
|
132
|
+
| PRO | cloud GPU | metered credit wallet (~1 credit ≈ 1 GPU-min, $99/100) | instant | yes (cloud) |
|
|
133
|
+
| ENTERPRISE | on-prem / custom | quote-based | instant | roadmap |
|
|
134
|
+
|
|
135
|
+
## What you get, honestly
|
|
136
|
+
|
|
137
|
+
| Regime | What it means | What you get |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| CERTIFIED | within envelope, error budget below target, **validated against a live QPU ground truth** | certified expectation + 9-term budget (requires a real `qpu_backend`) |
|
|
140
|
+
| UNCERTIFIED | simulated and consistency-checked, but no QPU was available to validate | honest value + budget, marked UNCERTIFIED |
|
|
141
|
+
| REJECTED | no engine envelope can simulate this circuit | `RejectionResult` with reason + reformulation suggestions, zero compute spent |
|
|
142
|
+
|
|
143
|
+
We never market "Certified" without a live QPU behind it, and we never claim
|
|
144
|
+
live QPU integration that isn't shipped.
|
|
145
|
+
|
|
146
|
+
## Topology support
|
|
147
|
+
|
|
148
|
+
`QTWISTBackend(topology=...)` and `predict(..., topology=...)` accept:
|
|
149
|
+
|
|
150
|
+
* `"1D_chain"` — Engine D (adaptive MPS) envelope, up to 1024 qubits
|
|
151
|
+
* `"2D_heavy_hex"` — shallow circuits via Engine C; deep heavy-hex is rejected with routing guidance
|
|
152
|
+
* `"all_to_all"` — fully connected, any engine that fits
|
|
153
|
+
* a Qiskit `CouplingMap` — edges are read from the map (1D chains are detected automatically)
|
|
154
|
+
|
|
155
|
+
## Verification
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
python prelaunch_audit.py # 61/61 correctness + honesty checks
|
|
159
|
+
python poc_scientist_programs.py # 10 real scientist programs
|
|
160
|
+
python poc_research_suite.py # 1024-qubit QEC via Engine C
|
|
161
|
+
python user_acceptance_test.py # 20-qubit TFIM sweep + 100-qubit rejection
|
|
162
|
+
python realworld_test.py # ordinary Qiskit programs vs exact reference
|
|
163
|
+
```
|
qtwist-5.0.0/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Q-TWIST v5.0 (Beta) — The Honest Oracle
|
|
2
|
+
|
|
3
|
+
Q-TWIST is a physics-constrained quantum-circuit inference engine that tells
|
|
4
|
+
you **what it can simulate, to what error, and whether your circuit is worth
|
|
5
|
+
running on real hardware** — and refuses, honestly, when it cannot. No silent
|
|
6
|
+
wrong numbers. No "certified digital twin" marketing. Rigorous L1 error bounds,
|
|
7
|
+
and an explicit refusal with a fix when a circuit is out of envelope.
|
|
8
|
+
|
|
9
|
+
It ships in two tiers:
|
|
10
|
+
|
|
11
|
+
* **FREE** — a local **`AerSimulator`-style drop-in** that runs on your laptop
|
|
12
|
+
CPU. Drop it into any Qiskit expectation-value workflow. No token, no bill.
|
|
13
|
+
* **PRO** — a **metered cloud GPU instance** unlocked by a premium token.
|
|
14
|
+
Costs are previewed *before* every run (IBM-style credit wallet). Adds the
|
|
15
|
+
**QPU-Readiness Oracle**: a plain-English verdict on whether your circuit is
|
|
16
|
+
good to submit to a real QPU.
|
|
17
|
+
|
|
18
|
+
> Live QPU routing is **roadmap**, not shipped. Everything today is classical
|
|
19
|
+
> simulation + an honest readiness verdict. See `qpu_backend.py` for the scaffold.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
python -m pip install qtwist
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
(Optional full-stack extras for Qiskit circuit input and the adaptive-MPS
|
|
28
|
+
engine: `python -m pip install -e ".[full]"`.)
|
|
29
|
+
|
|
30
|
+
## Quickstart — free local drop-in
|
|
31
|
+
|
|
32
|
+
You only need standard Qiskit. `QtwistSimulator` mirrors `AerSimulator`'s
|
|
33
|
+
`run()` / `result()` surface for the expectation-value workflow:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from qiskit import QuantumCircuit
|
|
37
|
+
from qtwist.aer_compat import QtwistSimulator
|
|
38
|
+
|
|
39
|
+
# 20-qubit transverse-field Ising model, 3 layers
|
|
40
|
+
qc = QuantumCircuit(20)
|
|
41
|
+
for _ in range(3):
|
|
42
|
+
for i in range(19):
|
|
43
|
+
qc.rzz(0.5, i, i + 1)
|
|
44
|
+
for i in range(20):
|
|
45
|
+
qc.rx(0.3, i)
|
|
46
|
+
|
|
47
|
+
sim = QtwistSimulator(instance="local") # free, laptop CPU
|
|
48
|
+
res = sim.run(qc, observable="Z0Z1", epsilon_target=0.05).result()
|
|
49
|
+
|
|
50
|
+
print(f"<Z0 Z1> = {res.value():+.6f} +/- {res.error:.4f}")
|
|
51
|
+
print(res.explain()) # QPU-Readiness Oracle verdict
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For small circuits (n ≤ 18) on the laptop you can also sample counts:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
counts = res.get_counts(shots=1024) # Qiskit bit-order (LSB-first)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## The QPU-Readiness Oracle
|
|
61
|
+
|
|
62
|
+
Every result carries a `qpu_readiness` verdict — the one thing you want to
|
|
63
|
+
know before spending real QPU queue time:
|
|
64
|
+
|
|
65
|
+
| Verdict | Meaning | What to do |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `READY_FOR_QPU` | Within Q-TWIST's validated envelope; value trustworthy within the reported L1 bound | Run it on hardware to validate the physics |
|
|
68
|
+
| `NEEDS_FIXES` | Rejected only for a fixable issue (long-range gate, multi-controlled gate, wrong topology) | Apply the listed suggestions, re-run |
|
|
69
|
+
| `INTRACTABLE` | Beyond classical simulation / QPU-advantage territory | Run on hardware directly if you must; Q-TWIST can't pre-validate |
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
print(res.explain())
|
|
73
|
+
# [QPU-Readiness: READY_FOR_QPU]
|
|
74
|
+
# Circuit is within Q-TWIST's validated envelope. The simulated value ...
|
|
75
|
+
# value=+0.993923 L1 bound=0.1287 cert=UNCERTIFIED (no live QPU validation)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Quickstart — premium cloud tier (metered)
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from qtwist.aer_compat import QtwistSimulator
|
|
82
|
+
from qtwist.pricing import Wallet
|
|
83
|
+
|
|
84
|
+
wallet = Wallet(token="PRO_TOKEN") # $99 base = 100 credits
|
|
85
|
+
sim = QtwistSimulator(instance="cloud:PRO_TOKEN", wallet=wallet, topology="1D_chain")
|
|
86
|
+
|
|
87
|
+
print(sim.estimate(qc)) # pre-flight cost, never bills
|
|
88
|
+
# [PRO / cloud] engine=ENGINE_D n=20 depth=39 -- est. 0.97 credits (~$0.96) ...
|
|
89
|
+
|
|
90
|
+
res = sim.run(qc, observable="Z0Z1").result() # charged only after confirm
|
|
91
|
+
print(res.explain())
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Tiers:
|
|
95
|
+
| Tier | Runs on | Billing | Capability Classifier | QPU-Readiness Oracle |
|
|
96
|
+
|---|---|---|---|---|
|
|
97
|
+
| FREE | laptop CPU | free | instant | yes (local) |
|
|
98
|
+
| PRO | cloud GPU | metered credit wallet (~1 credit ≈ 1 GPU-min, $99/100) | instant | yes (cloud) |
|
|
99
|
+
| ENTERPRISE | on-prem / custom | quote-based | instant | roadmap |
|
|
100
|
+
|
|
101
|
+
## What you get, honestly
|
|
102
|
+
|
|
103
|
+
| Regime | What it means | What you get |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| CERTIFIED | within envelope, error budget below target, **validated against a live QPU ground truth** | certified expectation + 9-term budget (requires a real `qpu_backend`) |
|
|
106
|
+
| UNCERTIFIED | simulated and consistency-checked, but no QPU was available to validate | honest value + budget, marked UNCERTIFIED |
|
|
107
|
+
| REJECTED | no engine envelope can simulate this circuit | `RejectionResult` with reason + reformulation suggestions, zero compute spent |
|
|
108
|
+
|
|
109
|
+
We never market "Certified" without a live QPU behind it, and we never claim
|
|
110
|
+
live QPU integration that isn't shipped.
|
|
111
|
+
|
|
112
|
+
## Topology support
|
|
113
|
+
|
|
114
|
+
`QTWISTBackend(topology=...)` and `predict(..., topology=...)` accept:
|
|
115
|
+
|
|
116
|
+
* `"1D_chain"` — Engine D (adaptive MPS) envelope, up to 1024 qubits
|
|
117
|
+
* `"2D_heavy_hex"` — shallow circuits via Engine C; deep heavy-hex is rejected with routing guidance
|
|
118
|
+
* `"all_to_all"` — fully connected, any engine that fits
|
|
119
|
+
* a Qiskit `CouplingMap` — edges are read from the map (1D chains are detected automatically)
|
|
120
|
+
|
|
121
|
+
## Verification
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
python prelaunch_audit.py # 61/61 correctness + honesty checks
|
|
125
|
+
python poc_scientist_programs.py # 10 real scientist programs
|
|
126
|
+
python poc_research_suite.py # 1024-qubit QEC via Engine C
|
|
127
|
+
python user_acceptance_test.py # 20-qubit TFIM sweep + 100-qubit rejection
|
|
128
|
+
python realworld_test.py # ordinary Qiskit programs vs exact reference
|
|
129
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "qtwist"
|
|
7
|
+
version = "5.0.0"
|
|
8
|
+
description = "Q-TWIST: certified-methodology digital twin for noisy quantum processors — engines, Python SDK, and web console."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "Proprietary" }
|
|
12
|
+
authors = [{ name = "Q-TWIST" }]
|
|
13
|
+
keywords = ["quantum", "simulation", "certified", "digital-twin", "noise"]
|
|
14
|
+
dependencies = [
|
|
15
|
+
"numpy>=2.0",
|
|
16
|
+
"scipy>=1.11",
|
|
17
|
+
"threadpoolctl>=3.0",
|
|
18
|
+
"qiskit>=1.0",
|
|
19
|
+
"qiskit-aer>=0.14",
|
|
20
|
+
"qiskit-ibm-runtime>=0.40",
|
|
21
|
+
"quimb>=1.8",
|
|
22
|
+
"fastapi>=0.110",
|
|
23
|
+
"uvicorn[standard]>=0.27",
|
|
24
|
+
"pydantic>=2.0",
|
|
25
|
+
"python-multipart>=0.0.9",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
torch = ["torch>=2.0"]
|
|
30
|
+
billing = ["razorpay>=2.0"]
|
|
31
|
+
full = ["torch>=2.0", "razorpay>=2.0"]
|
|
32
|
+
test = ["pytest>=8.0", "httpx>=0.27"]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
testpaths = ["tests"]
|
|
36
|
+
filterwarnings = [
|
|
37
|
+
"ignore::DeprecationWarning",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.scripts]
|
|
41
|
+
qtwist = "qtwist.cli:main"
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://qtwist.example"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.packages.find]
|
|
47
|
+
include = ["qtwist*", "qtwist_core*"]
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.package-data]
|
|
50
|
+
qtwist_core = ["certification/models/*.pt", "certification/models/*.json", "py.typed"]
|
|
51
|
+
qtwist = ["web/static/*.html", "web/static/*.css", "web/static/*.js"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Q-TWIST v5.0-beta — Python SDK Package
|
|
2
|
+
|
|
3
|
+
__version__ = "5.0.0-beta"
|
|
4
|
+
|
|
5
|
+
# Expose the engine packages (physics / router / core / certification) as
|
|
6
|
+
# top-level importable modules via the qtwist_core path shim.
|
|
7
|
+
import qtwist_core # noqa: F401
|
|
8
|
+
|
|
9
|
+
from qtwist.api import QTWISTBackend, PredictionResult, RejectionResult
|
|
10
|
+
|
|
11
|
+
__all__ = ["QTWISTBackend", "PredictionResult", "RejectionResult"]
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AerSimulator-compatible drop-in for Q-TWIST.
|
|
3
|
+
|
|
4
|
+
Free / local tier
|
|
5
|
+
-----------------
|
|
6
|
+
from qtwist.aer_compat import QtwistSimulator
|
|
7
|
+
|
|
8
|
+
sim = QtwistSimulator(instance="local")
|
|
9
|
+
job = sim.run(qc, observable="Z0Z1", epsilon_target=0.05)
|
|
10
|
+
res = job.result()
|
|
11
|
+
print(res.value()) # expectation <Z0Z1>
|
|
12
|
+
print(res.explain()) # QPU-Readiness Oracle verdict
|
|
13
|
+
counts = res.get_counts() # for small circuits (n <= 18) on the laptop
|
|
14
|
+
|
|
15
|
+
This is a behavioral drop-in for the *expectation-value* workflow that
|
|
16
|
+
Q-TWIST is built for. It deliberately does NOT pretend to be a generic
|
|
17
|
+
sampler for giant circuits -- that is what the paid cloud GPU tier is for.
|
|
18
|
+
|
|
19
|
+
Premium / cloud tier
|
|
20
|
+
--------------------
|
|
21
|
+
sim = QtwistSimulator(instance="cloud:PRO_TOKEN", topology="grid_2d")
|
|
22
|
+
res = sim.run(qc, observable="Z0Z1")
|
|
23
|
+
|
|
24
|
+
The same call returns the value + the QPU-Readiness Oracle verdict. Live
|
|
25
|
+
QPU routing is roadmap-only (see qpu_backend.py); everything here is
|
|
26
|
+
classical simulation + an honest readiness verdict.
|
|
27
|
+
"""
|
|
28
|
+
from typing import Optional
|
|
29
|
+
|
|
30
|
+
from qtwist.api import QTWISTBackend, PredictionResult, decompose_to_native
|
|
31
|
+
from qtwist.pricing import PricingEngine, Wallet, CostEstimate
|
|
32
|
+
from qtwist.payments import EntitlementStore, token_from_instance
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _entry_qubits(entry) -> tuple:
|
|
36
|
+
"""Extract the qubit indices carried by a GateEntry / tuple / entry."""
|
|
37
|
+
if isinstance(entry, (list, tuple)):
|
|
38
|
+
qubits = entry[1] if len(entry) > 1 else ()
|
|
39
|
+
return tuple(qubits) if isinstance(qubits, (list, tuple)) else (qubits,)
|
|
40
|
+
return tuple(getattr(entry, "qubits", ()))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _infer_n_qubits(circuit) -> int:
|
|
44
|
+
"""Number of qubits, including for native layer lists (no ``num_qubits``).
|
|
45
|
+
|
|
46
|
+
Layer-list circuits come in without a ``num_qubits`` attribute; billing
|
|
47
|
+
must not silently fall back to n=0 (which would floor-price every run).
|
|
48
|
+
The count is derived from the maximum qubit index used by a gate.
|
|
49
|
+
"""
|
|
50
|
+
if hasattr(circuit, "num_qubits") and circuit.num_qubits:
|
|
51
|
+
return int(circuit.num_qubits)
|
|
52
|
+
m = -1
|
|
53
|
+
layers = getattr(circuit, "data", circuit) or []
|
|
54
|
+
for layer in layers:
|
|
55
|
+
items = getattr(layer, "data", layer) or layer
|
|
56
|
+
for entry in items:
|
|
57
|
+
for q in _entry_qubits(entry):
|
|
58
|
+
try:
|
|
59
|
+
i = int(q)
|
|
60
|
+
except (TypeError, ValueError):
|
|
61
|
+
continue
|
|
62
|
+
if i > m:
|
|
63
|
+
m = i
|
|
64
|
+
return m + 1 if m >= 0 else 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class QtwistResult:
|
|
68
|
+
"""Aer-like result object returned by ``QtwistJob.result()``."""
|
|
69
|
+
|
|
70
|
+
def __init__(self, pred: PredictionResult, circuit=None, n_qubits: int = 0):
|
|
71
|
+
self._pred = pred
|
|
72
|
+
self._circuit = circuit
|
|
73
|
+
self._n = n_qubits
|
|
74
|
+
|
|
75
|
+
# --- Aer-compatible surface ---
|
|
76
|
+
def value(self):
|
|
77
|
+
"""Expectation value of the requested observable."""
|
|
78
|
+
return self._pred.value
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def status(self) -> str:
|
|
82
|
+
return self._pred.status
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def error(self) -> float:
|
|
86
|
+
return self._pred.epsilon_total
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def rejection_reason(self) -> Optional[str]:
|
|
90
|
+
return self._pred.rejection_reason
|
|
91
|
+
|
|
92
|
+
def reformulations(self):
|
|
93
|
+
return list(self._pred.reformulations)
|
|
94
|
+
|
|
95
|
+
# --- QPU-Readiness Oracle ---
|
|
96
|
+
def qpu_readiness(self) -> dict:
|
|
97
|
+
return self._pred.qpu_readiness
|
|
98
|
+
|
|
99
|
+
def explain(self) -> str:
|
|
100
|
+
return self._pred.explain()
|
|
101
|
+
|
|
102
|
+
# --- best-effort counts (small circuits only) ---
|
|
103
|
+
def get_counts(self, shots: int = 1024) -> dict:
|
|
104
|
+
"""
|
|
105
|
+
Sample Z-basis bitstring counts (local laptop tier).
|
|
106
|
+
|
|
107
|
+
Uses the noise-free TrajectorySampler (Engine A) statevector, which
|
|
108
|
+
returns counts in Qiskit bit order (qubit 0 = rightmost character).
|
|
109
|
+
|
|
110
|
+
Only available for ``n <= 18`` qubits -- beyond that, classical
|
|
111
|
+
wavefunction sampling is not what the free tier is for; use the
|
|
112
|
+
cloud GPU tier or stick to ``value()`` for the expectation.
|
|
113
|
+
"""
|
|
114
|
+
if self._circuit is None:
|
|
115
|
+
raise RuntimeError("counts not available for this result")
|
|
116
|
+
if self._n > 18:
|
|
117
|
+
raise RuntimeError(
|
|
118
|
+
f"get_counts() for n={self._n} requires the cloud GPU tier; "
|
|
119
|
+
f"use value() for the expectation on the local tier."
|
|
120
|
+
)
|
|
121
|
+
from qtwist.api import normalize_circuit
|
|
122
|
+
from core.engine_a import TrajectorySampler
|
|
123
|
+
from physics.lindbladian import LindbladianParams
|
|
124
|
+
|
|
125
|
+
ic, nq = normalize_circuit(decompose_to_native(self._circuit))
|
|
126
|
+
lp = LindbladianParams(
|
|
127
|
+
n_qubits=nq,
|
|
128
|
+
T1={i: 1e12 for i in range(nq)},
|
|
129
|
+
T2={i: 2e12 for i in range(nq)},
|
|
130
|
+
omega_q={i: 5.0 for i in range(nq)},
|
|
131
|
+
zz_coupling={}, cr_echo_IX={}, cr_echo_IY={},
|
|
132
|
+
spectator_phases={}, readout_xtalk={}, topology=[],
|
|
133
|
+
calibration_timestamp="noise-free-counts",
|
|
134
|
+
gate_errors={}, gate_durations={}, gate_errors_1q={},
|
|
135
|
+
readout_errors={},
|
|
136
|
+
)
|
|
137
|
+
sa = TrajectorySampler(nq, epsilon_target=1e-9)
|
|
138
|
+
for layer in ic:
|
|
139
|
+
for entry in layer:
|
|
140
|
+
sa.apply_gate_and_noise(
|
|
141
|
+
gate=entry[0], qubits=tuple(entry[1]),
|
|
142
|
+
lindbladian_params=lp, trajectory_seed=0,
|
|
143
|
+
angle=getattr(entry, "angle", None),
|
|
144
|
+
)
|
|
145
|
+
return sa.measure(shots=shots)
|
|
146
|
+
|
|
147
|
+
def __repr__(self) -> str:
|
|
148
|
+
return self._pred.certification_block
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class QtwistJob:
|
|
152
|
+
"""Aer-like job: ``sim.run(...).result()``."""
|
|
153
|
+
|
|
154
|
+
def __init__(self, result: QtwistResult):
|
|
155
|
+
self._result = result
|
|
156
|
+
|
|
157
|
+
def result(self) -> QtwistResult:
|
|
158
|
+
return self._result
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class QtwistSimulator:
|
|
162
|
+
"""
|
|
163
|
+
Drop-in replacement for ``qiskit_aer.AerSimulator`` for the
|
|
164
|
+
expectation-value workflow, with the QPU-Readiness Oracle attached.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
instance : str
|
|
169
|
+
"local" (free CPU tier) or "cloud:<TOKEN>" (paid cloud GPU tier).
|
|
170
|
+
api_token : str
|
|
171
|
+
Token for the cloud tier (ignored on "local").
|
|
172
|
+
topology : str | None
|
|
173
|
+
"chain_1d", "grid_2d", "all_to_all" -- hint for routing.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
def __init__(self, instance: str = "local", api_token: str = "none",
|
|
177
|
+
topology=None, wallet: Optional[Wallet] = None):
|
|
178
|
+
self.instance = instance
|
|
179
|
+
# Paid tiers (PRO / ENTERPRISE) both require a bought token + instance
|
|
180
|
+
# and are metered through a Wallet. The instance string carries the
|
|
181
|
+
# entitlement minted by ``RazorpayBilling.provision``; the api_token
|
|
182
|
+
# is the credential presented with it.
|
|
183
|
+
self.tier = PricingEngine.tier_for(instance)
|
|
184
|
+
self._premium = self.tier in ("pro", "enterprise")
|
|
185
|
+
token = api_token if self._premium else "none"
|
|
186
|
+
self.backend = QTWISTBackend(api_token=token, topology=topology)
|
|
187
|
+
self.wallet = wallet
|
|
188
|
+
self.entitlement = None
|
|
189
|
+
self._store = None
|
|
190
|
+
# Resolve a paid entitlement (if any) so a minted ``cloud:<TOKEN>``
|
|
191
|
+
# string binds a Wallet funded with the actually-purchased credits.
|
|
192
|
+
self._persist = False
|
|
193
|
+
if self._premium:
|
|
194
|
+
ent = EntitlementStore.default().resolve_instance(instance)
|
|
195
|
+
if ent is not None and ent.active:
|
|
196
|
+
self.entitlement = ent
|
|
197
|
+
self._store = EntitlementStore.default()
|
|
198
|
+
# A store-backed entitlement means every charged credit is
|
|
199
|
+
# persisted, whether the wallet was auto-built here or
|
|
200
|
+
# supplied by the caller (double-spend guard).
|
|
201
|
+
self._persist = True
|
|
202
|
+
if wallet is None:
|
|
203
|
+
self.wallet = Wallet(
|
|
204
|
+
token=ent.token, tier=ent.tier,
|
|
205
|
+
credits_remaining=ent.credits,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
def depth(self, circuit) -> int:
|
|
209
|
+
if hasattr(circuit, "depth"):
|
|
210
|
+
return circuit.depth()
|
|
211
|
+
return len(getattr(circuit, "data", circuit) or [])
|
|
212
|
+
|
|
213
|
+
def estimate(self, circuit, observable: str = "Z0Z1",
|
|
214
|
+
epsilon_target: float = 0.05) -> CostEstimate:
|
|
215
|
+
"""
|
|
216
|
+
Pre-flight cost preview (Honest Oracle promise): see the bill
|
|
217
|
+
BEFORE running. Never charges.
|
|
218
|
+
|
|
219
|
+
Uses the same decomposed circuit as ``run`` so the preview exactly
|
|
220
|
+
matches the charge the user will see.
|
|
221
|
+
"""
|
|
222
|
+
dec = decompose_to_native(circuit)
|
|
223
|
+
pred = self.backend.predict(
|
|
224
|
+
dec, observable=observable, epsilon_target=epsilon_target
|
|
225
|
+
)
|
|
226
|
+
nq = _infer_n_qubits(dec)
|
|
227
|
+
return PricingEngine.estimate(
|
|
228
|
+
self.instance, pred.engine, nq, self.depth(dec)
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
def run(self, circuit, observable: str = "Z0Z1",
|
|
232
|
+
epsilon_target: float = 0.05, charge: bool = True) -> QtwistJob:
|
|
233
|
+
# Fail CLOSED on billing: a paid tier never runs for free. Without
|
|
234
|
+
# a funded wallet (no active entitlement, or a caller wallet with no
|
|
235
|
+
# credits) there is nothing to meter, so refuse before computing.
|
|
236
|
+
if self._premium and charge and self.wallet is None:
|
|
237
|
+
raise RuntimeError(
|
|
238
|
+
"PRO/ENTERPRISE run requires a funded wallet: this instance "
|
|
239
|
+
"does not match an active paid entitlement and no wallet was "
|
|
240
|
+
"supplied. Use estimate() for a pre-flight preview, or top up "
|
|
241
|
+
"via the Q-TWIST billing portal (Razorpay)."
|
|
242
|
+
)
|
|
243
|
+
# Intercept non-native gates (SWAP, RXX, ...) before the engines.
|
|
244
|
+
circuit = decompose_to_native(circuit)
|
|
245
|
+
pred = self.backend.predict(
|
|
246
|
+
circuit, observable=observable, epsilon_target=epsilon_target
|
|
247
|
+
)
|
|
248
|
+
nq = _infer_n_qubits(circuit)
|
|
249
|
+
# cloud tier: charge the pre-flight estimate before returning
|
|
250
|
+
if self._premium and charge:
|
|
251
|
+
# Never bill a run that was REJECTED -- no compute was delivered.
|
|
252
|
+
if not pred.rejection_reason:
|
|
253
|
+
est = PricingEngine.estimate(
|
|
254
|
+
self.instance, pred.engine, nq, self.depth(circuit)
|
|
255
|
+
)
|
|
256
|
+
if est.credits > 0:
|
|
257
|
+
self.wallet.charge(est.credits)
|
|
258
|
+
# Persist the consumed balance so re-instantiating the
|
|
259
|
+
# simulator does not re-grant the full purchased credits
|
|
260
|
+
# (double-spend guard).
|
|
261
|
+
if self._persist and self.entitlement is not None:
|
|
262
|
+
self._store.decrement(
|
|
263
|
+
self.entitlement.token, est.credits
|
|
264
|
+
)
|
|
265
|
+
return QtwistJob(QtwistResult(pred, circuit=circuit, n_qubits=nq))
|