fqkit 0.1.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.
- fqkit-0.1.0/LICENSE +21 -0
- fqkit-0.1.0/PKG-INFO +237 -0
- fqkit-0.1.0/README.md +202 -0
- fqkit-0.1.0/fqkit/__init__.py +59 -0
- fqkit-0.1.0/fqkit/core/__init__.py +42 -0
- fqkit-0.1.0/fqkit/core/circuit.py +40 -0
- fqkit-0.1.0/fqkit/core/gate.py +76 -0
- fqkit-0.1.0/fqkit/core/measurement.py +33 -0
- fqkit-0.1.0/fqkit/core/operation.py +15 -0
- fqkit-0.1.0/fqkit/core/parameter.py +11 -0
- fqkit-0.1.0/fqkit/core/parameter_binding.py +13 -0
- fqkit-0.1.0/fqkit/core/qasm.py +93 -0
- fqkit-0.1.0/fqkit/core/qubit.py +14 -0
- fqkit-0.1.0/fqkit/core/simulator.py +106 -0
- fqkit-0.1.0/fqkit/hardware/__init__.py +174 -0
- fqkit-0.1.0/fqkit/hardware/braket.py +96 -0
- fqkit-0.1.0/fqkit/hardware/errors.py +17 -0
- fqkit-0.1.0/fqkit/hardware/ibm.py +131 -0
- fqkit-0.1.0/fqkit/hardware/job.py +102 -0
- fqkit-0.1.0/fqkit/hardware/machines.py +28 -0
- fqkit-0.1.0/fqkit/hardware/qasm3.py +51 -0
- fqkit-0.1.0/fqkit.egg-info/PKG-INFO +237 -0
- fqkit-0.1.0/fqkit.egg-info/SOURCES.txt +29 -0
- fqkit-0.1.0/fqkit.egg-info/dependency_links.txt +1 -0
- fqkit-0.1.0/fqkit.egg-info/requires.txt +16 -0
- fqkit-0.1.0/fqkit.egg-info/top_level.txt +1 -0
- fqkit-0.1.0/pyproject.toml +57 -0
- fqkit-0.1.0/setup.cfg +4 -0
- fqkit-0.1.0/tests/test_fqkit.py +195 -0
- fqkit-0.1.0/tests/test_hardware.py +343 -0
- fqkit-0.1.0/tests/test_qasm.py +136 -0
fqkit-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Felix
|
|
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.
|
fqkit-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fqkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight Python framework for building and simulating quantum circuits.
|
|
5
|
+
Author-email: Felix <felixo6996@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Felixowusu20/Fqkit
|
|
8
|
+
Keywords: quantum,quantum-computing,simulator,education,qiskit
|
|
9
|
+
Classifier: Intended Audience :: Education
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: numpy>=1.21
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
25
|
+
Provides-Extra: ibm
|
|
26
|
+
Requires-Dist: qiskit>=1.3; extra == "ibm"
|
|
27
|
+
Requires-Dist: qiskit-ibm-runtime>=0.30; extra == "ibm"
|
|
28
|
+
Provides-Extra: braket
|
|
29
|
+
Requires-Dist: amazon-braket-sdk>=1.88; extra == "braket"
|
|
30
|
+
Provides-Extra: hardware
|
|
31
|
+
Requires-Dist: qiskit>=1.3; extra == "hardware"
|
|
32
|
+
Requires-Dist: qiskit-ibm-runtime>=0.30; extra == "hardware"
|
|
33
|
+
Requires-Dist: amazon-braket-sdk>=1.88; extra == "hardware"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# FQkit
|
|
37
|
+
|
|
38
|
+
FQkit is a small Python framework for building and simulating quantum
|
|
39
|
+
circuits. The source stays short on purpose, so you can learn how a quantum
|
|
40
|
+
computer works by reading it and changing it.
|
|
41
|
+
|
|
42
|
+
> **Documentation website:** the full docs live in [`website/`](website): a
|
|
43
|
+
> Next.js + Nextra site. Run it locally with `cd website && npm install && npm
|
|
44
|
+
> run dev`, or deploy it for free on Vercel (see the
|
|
45
|
+
> [website README](website/README.md)).
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- Qubits and parameterized gates (`H`, `RX`, `RY`, `RZ`)
|
|
50
|
+
- Multi-qubit gates: `CNOT`, `CZ`, `SWAP`, `Toffoli`
|
|
51
|
+
- Circuit construction with `QuantumCircuit`
|
|
52
|
+
- Parameter binding for variational circuits
|
|
53
|
+
- A statevector simulator that returns the final state
|
|
54
|
+
- Measurement with shot-based sampling
|
|
55
|
+
- Export to OpenQASM 2.0: run your circuits on real IBM hardware via Qiskit
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install fqkit
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
FQkit needs Python 3.9 or newer. NumPy is installed with it.
|
|
64
|
+
|
|
65
|
+
To work on the source, clone the repository and install it in editable mode:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/Felixowusu20/Fqkit.git
|
|
69
|
+
cd Fqkit
|
|
70
|
+
pip install -e ".[dev]"
|
|
71
|
+
pytest
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Quick start
|
|
75
|
+
|
|
76
|
+
Build a Bell state, an entangled pair of qubits, and measure it:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from fqkit import QuantumCircuit, Hadamard, CNOT, run, measure_all
|
|
80
|
+
|
|
81
|
+
qc = QuantumCircuit(2)
|
|
82
|
+
qc.add_gate(Hadamard(), [0]) # put qubit 0 into superposition
|
|
83
|
+
qc.add_gate(CNOT(), [0, 1]) # entangle qubit 1 with qubit 0
|
|
84
|
+
|
|
85
|
+
state = run(qc) # -> array([0.707, 0, 0, 0.707])
|
|
86
|
+
counts = measure_all(state, shots=1024)
|
|
87
|
+
|
|
88
|
+
print("State :", state)
|
|
89
|
+
print("Counts:", counts) # -> {'00': ~512, '11': ~512}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Variational circuits
|
|
93
|
+
|
|
94
|
+
Gates can take symbolic `Parameter`s that you bind to numbers later: the basis
|
|
95
|
+
of variational algorithms like VQE and QAOA:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from fqkit import QuantumCircuit, Hadamard, RX, Parameter, bind_parameters, run, measure_all
|
|
99
|
+
|
|
100
|
+
theta = Parameter("theta")
|
|
101
|
+
|
|
102
|
+
qc = QuantumCircuit(2)
|
|
103
|
+
qc.add_gate(Hadamard(), [0])
|
|
104
|
+
qc.add_gate(RX(theta), [1])
|
|
105
|
+
|
|
106
|
+
bind_parameters(qc, {"theta": 3.14159})
|
|
107
|
+
|
|
108
|
+
counts = measure_all(run(qc), shots=1024)
|
|
109
|
+
print(counts)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Export to OpenQASM: run on real hardware
|
|
113
|
+
|
|
114
|
+
Any circuit can be exported to OpenQASM 2.0, the open standard that Qiskit
|
|
115
|
+
reads. That means a circuit you build in fqkit can run on a **real quantum
|
|
116
|
+
computer** through IBM Quantum's free tier: no hardware of your own needed.
|
|
117
|
+
Everything in this pipeline (OpenQASM, Qiskit, the IBM free tier) costs nothing.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from fqkit import QuantumCircuit, Hadamard, CNOT
|
|
121
|
+
|
|
122
|
+
qc = QuantumCircuit(2)
|
|
123
|
+
qc.add_gate(Hadamard(), [0])
|
|
124
|
+
qc.add_gate(CNOT(), [0, 1])
|
|
125
|
+
|
|
126
|
+
print(qc.to_qasm()) # or: to_qasm(qc)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
OPENQASM 2.0;
|
|
131
|
+
include "qelib1.inc";
|
|
132
|
+
qreg q[2];
|
|
133
|
+
creg c[2];
|
|
134
|
+
h q[0];
|
|
135
|
+
cx q[0], q[1];
|
|
136
|
+
measure q -> c;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Load it in Qiskit (free)
|
|
140
|
+
|
|
141
|
+
`pip install qiskit` (free and open source), then:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from qiskit import QuantumCircuit as QiskitCircuit
|
|
145
|
+
from qiskit.quantum_info import Statevector
|
|
146
|
+
|
|
147
|
+
qiskit_qc = QiskitCircuit.from_qasm_str(qc.to_qasm(measure=False))
|
|
148
|
+
print(Statevector.from_instruction(qiskit_qc).probabilities_dict())
|
|
149
|
+
# {'00': 0.5, '11': 0.5}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Run on real hardware
|
|
153
|
+
|
|
154
|
+
`run()` stays on your computer. Hardware jobs go through `fqkit.hardware`,
|
|
155
|
+
which calls the vendor SDK, waits on the queue, and returns counts in fqkit
|
|
156
|
+
bit order (qubit 0 on the left). The vendor packages are optional, so a normal
|
|
157
|
+
install still depends only on NumPy.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
pip install "fqkit[ibm]" # IBM Quantum, through Qiskit Runtime
|
|
161
|
+
pip install "fqkit[braket]" # IonQ, Rigetti, IQM, and AQT, through Amazon Braket
|
|
162
|
+
pip install "fqkit[hardware]" # both
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from fqkit.hardware import providers, submit
|
|
167
|
+
|
|
168
|
+
providers() # ibm, ionq, rigetti, iqm, aqt
|
|
169
|
+
|
|
170
|
+
job = submit(qc, "ibm", shots=1024)
|
|
171
|
+
print(job.job_id, job.status()) # QUEUED, RUNNING, COMPLETED, CANCELLED, FAILED
|
|
172
|
+
print(job.counts()) # waits for the device
|
|
173
|
+
|
|
174
|
+
job = submit(qc, "ionq", shots=100)
|
|
175
|
+
job = submit(qc, "rigetti", shots=100)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
IBM needs a free account at https://quantum.cloud.ibm.com. Save the token once
|
|
179
|
+
with `QiskitRuntimeService.save_account`, or pass `token=` to `submit`. With
|
|
180
|
+
no backend name, fqkit picks the least busy real device. Pass
|
|
181
|
+
`backend="ibm_..."` to choose one, or `backends("ibm", live=True)` to list
|
|
182
|
+
the devices that are up.
|
|
183
|
+
|
|
184
|
+
IonQ, Rigetti, IQM, and AQT are one Amazon Braket account. Those QPU tasks are
|
|
185
|
+
billed by AWS. `submit(qc, "ionq")` targets IonQ Forte-1. Pass `backend=` as a
|
|
186
|
+
full device ARN when you need a different chip. `job.status()` returns
|
|
187
|
+
immediately. `job.counts()` blocks until the machine finishes. `get_job(machine,
|
|
188
|
+
job_id)` reconnects to a job you already submitted.
|
|
189
|
+
|
|
190
|
+
The lessons in the browser keep using the local simulator. A hardware token
|
|
191
|
+
stays in your own Python process.
|
|
192
|
+
|
|
193
|
+
## Project layout
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
fqkit/
|
|
197
|
+
core/
|
|
198
|
+
qubit.py # Qubit: an index into Hilbert space
|
|
199
|
+
parameter.py # Parameter: a symbolic variable
|
|
200
|
+
gate.py # Gate + H, RX, RY, RZ, CNOT, CZ, SWAP, Toffoli
|
|
201
|
+
operation.py # Operation: a gate applied to specific qubits
|
|
202
|
+
circuit.py # QuantumCircuit: an ordered list of operations
|
|
203
|
+
parameter_binding.py # bind_parameters: substitute symbols -> numbers
|
|
204
|
+
simulator.py # run: apply gates to a statevector
|
|
205
|
+
measurement.py # measure_all: sample |amplitude|^2
|
|
206
|
+
qasm.py # to_qasm: export circuits to OpenQASM 2.0
|
|
207
|
+
hardware/
|
|
208
|
+
ibm.py # IBM Quantum via Qiskit Runtime
|
|
209
|
+
braket.py # IonQ, Rigetti, IQM, AQT via Amazon Braket
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Convention
|
|
213
|
+
|
|
214
|
+
FQkit uses **big-endian** qubit ordering: qubit 0 is the most significant bit
|
|
215
|
+
of the state-vector index, and the first qubit passed to a multi-qubit gate is
|
|
216
|
+
its most significant qubit (for `CNOT`, the control).
|
|
217
|
+
|
|
218
|
+
## Testing
|
|
219
|
+
|
|
220
|
+
FQkit ships with a `pytest` suite covering the simulator, every gate, parameter
|
|
221
|
+
binding, measurement, input validation, and the OpenQASM export, including
|
|
222
|
+
round-trip tests that load an exported circuit into Qiskit and check that the
|
|
223
|
+
statevectors match fqkit's own simulator.
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
pip install -e ".[dev]" # installs pytest
|
|
227
|
+
pytest -v # 46 tests
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The Qiskit round-trip tests are skipped automatically if Qiskit is not
|
|
231
|
+
installed. Install it (free) with `pip install qiskit` to run those too.
|
|
232
|
+
|
|
233
|
+
| Test file | Covers |
|
|
234
|
+
|---|---|
|
|
235
|
+
| `tests/test_fqkit.py` | Single- and multi-qubit gates, entanglement (Bell states), reversed and non-adjacent controls, parameter binding, unbound-parameter errors, measurement statistics, and input validation |
|
|
236
|
+
| `tests/test_qasm.py` | OpenQASM string output, gate-name mapping, error handling, and Qiskit round-trip equivalence |
|
|
237
|
+
| `tests/test_hardware.py` | Machine selection, OpenQASM 3 for Braket, Runtime and Braket submission with stand-in SDKs, job status, and bit order |
|
fqkit-0.1.0/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# FQkit
|
|
2
|
+
|
|
3
|
+
FQkit is a small Python framework for building and simulating quantum
|
|
4
|
+
circuits. The source stays short on purpose, so you can learn how a quantum
|
|
5
|
+
computer works by reading it and changing it.
|
|
6
|
+
|
|
7
|
+
> **Documentation website:** the full docs live in [`website/`](website): a
|
|
8
|
+
> Next.js + Nextra site. Run it locally with `cd website && npm install && npm
|
|
9
|
+
> run dev`, or deploy it for free on Vercel (see the
|
|
10
|
+
> [website README](website/README.md)).
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- Qubits and parameterized gates (`H`, `RX`, `RY`, `RZ`)
|
|
15
|
+
- Multi-qubit gates: `CNOT`, `CZ`, `SWAP`, `Toffoli`
|
|
16
|
+
- Circuit construction with `QuantumCircuit`
|
|
17
|
+
- Parameter binding for variational circuits
|
|
18
|
+
- A statevector simulator that returns the final state
|
|
19
|
+
- Measurement with shot-based sampling
|
|
20
|
+
- Export to OpenQASM 2.0: run your circuits on real IBM hardware via Qiskit
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install fqkit
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
FQkit needs Python 3.9 or newer. NumPy is installed with it.
|
|
29
|
+
|
|
30
|
+
To work on the source, clone the repository and install it in editable mode:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git clone https://github.com/Felixowusu20/Fqkit.git
|
|
34
|
+
cd Fqkit
|
|
35
|
+
pip install -e ".[dev]"
|
|
36
|
+
pytest
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
Build a Bell state, an entangled pair of qubits, and measure it:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from fqkit import QuantumCircuit, Hadamard, CNOT, run, measure_all
|
|
45
|
+
|
|
46
|
+
qc = QuantumCircuit(2)
|
|
47
|
+
qc.add_gate(Hadamard(), [0]) # put qubit 0 into superposition
|
|
48
|
+
qc.add_gate(CNOT(), [0, 1]) # entangle qubit 1 with qubit 0
|
|
49
|
+
|
|
50
|
+
state = run(qc) # -> array([0.707, 0, 0, 0.707])
|
|
51
|
+
counts = measure_all(state, shots=1024)
|
|
52
|
+
|
|
53
|
+
print("State :", state)
|
|
54
|
+
print("Counts:", counts) # -> {'00': ~512, '11': ~512}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Variational circuits
|
|
58
|
+
|
|
59
|
+
Gates can take symbolic `Parameter`s that you bind to numbers later: the basis
|
|
60
|
+
of variational algorithms like VQE and QAOA:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from fqkit import QuantumCircuit, Hadamard, RX, Parameter, bind_parameters, run, measure_all
|
|
64
|
+
|
|
65
|
+
theta = Parameter("theta")
|
|
66
|
+
|
|
67
|
+
qc = QuantumCircuit(2)
|
|
68
|
+
qc.add_gate(Hadamard(), [0])
|
|
69
|
+
qc.add_gate(RX(theta), [1])
|
|
70
|
+
|
|
71
|
+
bind_parameters(qc, {"theta": 3.14159})
|
|
72
|
+
|
|
73
|
+
counts = measure_all(run(qc), shots=1024)
|
|
74
|
+
print(counts)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Export to OpenQASM: run on real hardware
|
|
78
|
+
|
|
79
|
+
Any circuit can be exported to OpenQASM 2.0, the open standard that Qiskit
|
|
80
|
+
reads. That means a circuit you build in fqkit can run on a **real quantum
|
|
81
|
+
computer** through IBM Quantum's free tier: no hardware of your own needed.
|
|
82
|
+
Everything in this pipeline (OpenQASM, Qiskit, the IBM free tier) costs nothing.
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from fqkit import QuantumCircuit, Hadamard, CNOT
|
|
86
|
+
|
|
87
|
+
qc = QuantumCircuit(2)
|
|
88
|
+
qc.add_gate(Hadamard(), [0])
|
|
89
|
+
qc.add_gate(CNOT(), [0, 1])
|
|
90
|
+
|
|
91
|
+
print(qc.to_qasm()) # or: to_qasm(qc)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
OPENQASM 2.0;
|
|
96
|
+
include "qelib1.inc";
|
|
97
|
+
qreg q[2];
|
|
98
|
+
creg c[2];
|
|
99
|
+
h q[0];
|
|
100
|
+
cx q[0], q[1];
|
|
101
|
+
measure q -> c;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Load it in Qiskit (free)
|
|
105
|
+
|
|
106
|
+
`pip install qiskit` (free and open source), then:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from qiskit import QuantumCircuit as QiskitCircuit
|
|
110
|
+
from qiskit.quantum_info import Statevector
|
|
111
|
+
|
|
112
|
+
qiskit_qc = QiskitCircuit.from_qasm_str(qc.to_qasm(measure=False))
|
|
113
|
+
print(Statevector.from_instruction(qiskit_qc).probabilities_dict())
|
|
114
|
+
# {'00': 0.5, '11': 0.5}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Run on real hardware
|
|
118
|
+
|
|
119
|
+
`run()` stays on your computer. Hardware jobs go through `fqkit.hardware`,
|
|
120
|
+
which calls the vendor SDK, waits on the queue, and returns counts in fqkit
|
|
121
|
+
bit order (qubit 0 on the left). The vendor packages are optional, so a normal
|
|
122
|
+
install still depends only on NumPy.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install "fqkit[ibm]" # IBM Quantum, through Qiskit Runtime
|
|
126
|
+
pip install "fqkit[braket]" # IonQ, Rigetti, IQM, and AQT, through Amazon Braket
|
|
127
|
+
pip install "fqkit[hardware]" # both
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from fqkit.hardware import providers, submit
|
|
132
|
+
|
|
133
|
+
providers() # ibm, ionq, rigetti, iqm, aqt
|
|
134
|
+
|
|
135
|
+
job = submit(qc, "ibm", shots=1024)
|
|
136
|
+
print(job.job_id, job.status()) # QUEUED, RUNNING, COMPLETED, CANCELLED, FAILED
|
|
137
|
+
print(job.counts()) # waits for the device
|
|
138
|
+
|
|
139
|
+
job = submit(qc, "ionq", shots=100)
|
|
140
|
+
job = submit(qc, "rigetti", shots=100)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
IBM needs a free account at https://quantum.cloud.ibm.com. Save the token once
|
|
144
|
+
with `QiskitRuntimeService.save_account`, or pass `token=` to `submit`. With
|
|
145
|
+
no backend name, fqkit picks the least busy real device. Pass
|
|
146
|
+
`backend="ibm_..."` to choose one, or `backends("ibm", live=True)` to list
|
|
147
|
+
the devices that are up.
|
|
148
|
+
|
|
149
|
+
IonQ, Rigetti, IQM, and AQT are one Amazon Braket account. Those QPU tasks are
|
|
150
|
+
billed by AWS. `submit(qc, "ionq")` targets IonQ Forte-1. Pass `backend=` as a
|
|
151
|
+
full device ARN when you need a different chip. `job.status()` returns
|
|
152
|
+
immediately. `job.counts()` blocks until the machine finishes. `get_job(machine,
|
|
153
|
+
job_id)` reconnects to a job you already submitted.
|
|
154
|
+
|
|
155
|
+
The lessons in the browser keep using the local simulator. A hardware token
|
|
156
|
+
stays in your own Python process.
|
|
157
|
+
|
|
158
|
+
## Project layout
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
fqkit/
|
|
162
|
+
core/
|
|
163
|
+
qubit.py # Qubit: an index into Hilbert space
|
|
164
|
+
parameter.py # Parameter: a symbolic variable
|
|
165
|
+
gate.py # Gate + H, RX, RY, RZ, CNOT, CZ, SWAP, Toffoli
|
|
166
|
+
operation.py # Operation: a gate applied to specific qubits
|
|
167
|
+
circuit.py # QuantumCircuit: an ordered list of operations
|
|
168
|
+
parameter_binding.py # bind_parameters: substitute symbols -> numbers
|
|
169
|
+
simulator.py # run: apply gates to a statevector
|
|
170
|
+
measurement.py # measure_all: sample |amplitude|^2
|
|
171
|
+
qasm.py # to_qasm: export circuits to OpenQASM 2.0
|
|
172
|
+
hardware/
|
|
173
|
+
ibm.py # IBM Quantum via Qiskit Runtime
|
|
174
|
+
braket.py # IonQ, Rigetti, IQM, AQT via Amazon Braket
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Convention
|
|
178
|
+
|
|
179
|
+
FQkit uses **big-endian** qubit ordering: qubit 0 is the most significant bit
|
|
180
|
+
of the state-vector index, and the first qubit passed to a multi-qubit gate is
|
|
181
|
+
its most significant qubit (for `CNOT`, the control).
|
|
182
|
+
|
|
183
|
+
## Testing
|
|
184
|
+
|
|
185
|
+
FQkit ships with a `pytest` suite covering the simulator, every gate, parameter
|
|
186
|
+
binding, measurement, input validation, and the OpenQASM export, including
|
|
187
|
+
round-trip tests that load an exported circuit into Qiskit and check that the
|
|
188
|
+
statevectors match fqkit's own simulator.
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
pip install -e ".[dev]" # installs pytest
|
|
192
|
+
pytest -v # 46 tests
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The Qiskit round-trip tests are skipped automatically if Qiskit is not
|
|
196
|
+
installed. Install it (free) with `pip install qiskit` to run those too.
|
|
197
|
+
|
|
198
|
+
| Test file | Covers |
|
|
199
|
+
|---|---|
|
|
200
|
+
| `tests/test_fqkit.py` | Single- and multi-qubit gates, entanglement (Bell states), reversed and non-adjacent controls, parameter binding, unbound-parameter errors, measurement statistics, and input validation |
|
|
201
|
+
| `tests/test_qasm.py` | OpenQASM string output, gate-name mapping, error handling, and Qiskit round-trip equivalence |
|
|
202
|
+
| `tests/test_hardware.py` | Machine selection, OpenQASM 3 for Braket, Runtime and Braket submission with stand-in SDKs, job status, and bit order |
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FQkit — a lightweight quantum circuit framework for learning and simulation.
|
|
3
|
+
|
|
4
|
+
Quick start
|
|
5
|
+
-----------
|
|
6
|
+
from fqkit import QuantumCircuit, Hadamard, CNOT, run, measure_all
|
|
7
|
+
|
|
8
|
+
qc = QuantumCircuit(2)
|
|
9
|
+
qc.add_gate(Hadamard(), [0])
|
|
10
|
+
qc.add_gate(CNOT(), [0, 1])
|
|
11
|
+
|
|
12
|
+
state = run(qc)
|
|
13
|
+
counts = measure_all(state, shots=1024)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from fqkit.core import (
|
|
17
|
+
Qubit,
|
|
18
|
+
Parameter,
|
|
19
|
+
Gate,
|
|
20
|
+
Hadamard,
|
|
21
|
+
RX,
|
|
22
|
+
RY,
|
|
23
|
+
RZ,
|
|
24
|
+
CNOT,
|
|
25
|
+
CZ,
|
|
26
|
+
SWAP,
|
|
27
|
+
Toffoli,
|
|
28
|
+
Operation,
|
|
29
|
+
QuantumCircuit,
|
|
30
|
+
bind_parameters,
|
|
31
|
+
run,
|
|
32
|
+
apply_gate,
|
|
33
|
+
measure_all,
|
|
34
|
+
to_qasm,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
__version__ = "0.1.0"
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"Qubit",
|
|
41
|
+
"Parameter",
|
|
42
|
+
"Gate",
|
|
43
|
+
"Hadamard",
|
|
44
|
+
"RX",
|
|
45
|
+
"RY",
|
|
46
|
+
"RZ",
|
|
47
|
+
"CNOT",
|
|
48
|
+
"CZ",
|
|
49
|
+
"SWAP",
|
|
50
|
+
"Toffoli",
|
|
51
|
+
"Operation",
|
|
52
|
+
"QuantumCircuit",
|
|
53
|
+
"bind_parameters",
|
|
54
|
+
"run",
|
|
55
|
+
"apply_gate",
|
|
56
|
+
"measure_all",
|
|
57
|
+
"to_qasm",
|
|
58
|
+
"__version__",
|
|
59
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""fqkit.core — the building blocks of an fqkit quantum circuit."""
|
|
2
|
+
|
|
3
|
+
from fqkit.core.qubit import Qubit
|
|
4
|
+
from fqkit.core.parameter import Parameter
|
|
5
|
+
from fqkit.core.gate import (
|
|
6
|
+
Gate,
|
|
7
|
+
Hadamard,
|
|
8
|
+
RX,
|
|
9
|
+
RY,
|
|
10
|
+
RZ,
|
|
11
|
+
CNOT,
|
|
12
|
+
CZ,
|
|
13
|
+
SWAP,
|
|
14
|
+
Toffoli,
|
|
15
|
+
)
|
|
16
|
+
from fqkit.core.operation import Operation
|
|
17
|
+
from fqkit.core.circuit import QuantumCircuit
|
|
18
|
+
from fqkit.core.parameter_binding import bind_parameters
|
|
19
|
+
from fqkit.core.simulator import run, apply_gate
|
|
20
|
+
from fqkit.core.measurement import measure_all
|
|
21
|
+
from fqkit.core.qasm import to_qasm
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"Qubit",
|
|
25
|
+
"Parameter",
|
|
26
|
+
"Gate",
|
|
27
|
+
"Hadamard",
|
|
28
|
+
"RX",
|
|
29
|
+
"RY",
|
|
30
|
+
"RZ",
|
|
31
|
+
"CNOT",
|
|
32
|
+
"CZ",
|
|
33
|
+
"SWAP",
|
|
34
|
+
"Toffoli",
|
|
35
|
+
"Operation",
|
|
36
|
+
"QuantumCircuit",
|
|
37
|
+
"bind_parameters",
|
|
38
|
+
"run",
|
|
39
|
+
"apply_gate",
|
|
40
|
+
"measure_all",
|
|
41
|
+
"to_qasm",
|
|
42
|
+
]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from fqkit.core.operation import Operation
|
|
2
|
+
from fqkit.core.qubit import Qubit
|
|
3
|
+
from fqkit.core.qasm import to_qasm as _to_qasm
|
|
4
|
+
|
|
5
|
+
class QuantumCircuit:
|
|
6
|
+
def __init__(self , num_qubits:int):
|
|
7
|
+
if num_qubits <= 0:
|
|
8
|
+
raise ValueError("A circuit needs at least one qubit.")
|
|
9
|
+
self.num_qubits = num_qubits
|
|
10
|
+
self.qubits = [Qubit(i) for i in range(num_qubits)]
|
|
11
|
+
self.operations = []
|
|
12
|
+
|
|
13
|
+
def add_gate(self, gate, target_qubits):
|
|
14
|
+
# Convert Qubit objects to indices automatically
|
|
15
|
+
target_qubits = [q.index if isinstance(q, Qubit) else q for q in target_qubits]
|
|
16
|
+
|
|
17
|
+
if len(target_qubits) != gate.num_qubits:
|
|
18
|
+
raise ValueError(f"Gate {gate.name} requires {gate.num_qubits} qubits, "
|
|
19
|
+
f"but {len(target_qubits)} were provided.")
|
|
20
|
+
if min(target_qubits) < 0:
|
|
21
|
+
raise ValueError("Target qubit index cannot be negative")
|
|
22
|
+
if max(target_qubits) >= self.num_qubits:
|
|
23
|
+
raise ValueError("Target qubit index exceeds circuit size")
|
|
24
|
+
if len(set(target_qubits)) != len(target_qubits):
|
|
25
|
+
raise ValueError(f"Gate {gate.name} was given a repeated target qubit: "
|
|
26
|
+
f"{target_qubits}")
|
|
27
|
+
|
|
28
|
+
op = Operation(gate, target_qubits)
|
|
29
|
+
self.operations.append(op)
|
|
30
|
+
|
|
31
|
+
def to_qasm(self, measure=True):
|
|
32
|
+
"""Return this circuit as an OpenQASM 2.0 string.
|
|
33
|
+
|
|
34
|
+
See fqkit.core.qasm.to_qasm for details.
|
|
35
|
+
"""
|
|
36
|
+
return _to_qasm(self, measure=measure)
|
|
37
|
+
|
|
38
|
+
def __repr__(self):
|
|
39
|
+
op_str = '\n'.join([str(op) for op in self.operations])
|
|
40
|
+
return f"QuantumCircuit({self.num_qubits}, [\n{op_str}\n])"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Gates (unitary operators)
|
|
2
|
+
# A gate is a unitary matrix: U such that U†U = I.
|
|
3
|
+
# Some gates are parameterized.
|
|
4
|
+
|
|
5
|
+
# BASE GATE CLASS
|
|
6
|
+
|
|
7
|
+
class Gate:
|
|
8
|
+
def __init__(self ,name:str ,num_qubits:int ,matrix = None ,params = None):
|
|
9
|
+
self.name = name
|
|
10
|
+
self.num_qubits = num_qubits
|
|
11
|
+
self.matrix = matrix
|
|
12
|
+
self.params = params or []
|
|
13
|
+
|
|
14
|
+
def __repr__(self):
|
|
15
|
+
if self.params:
|
|
16
|
+
params = ', '.join([str(p) for p in self.params])
|
|
17
|
+
return f"{self.name}({params})"
|
|
18
|
+
else:
|
|
19
|
+
return f"{self.name}"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# # let try creating some gates (Hadamard, CNOT, RX , Toffoli , swap and the rest will be added)
|
|
24
|
+
def Hadamard():
|
|
25
|
+
return Gate(name="H", num_qubits=1, matrix=[[1/2**0.5, 1/2**0.5], [1/2**0.5, -1/2**0.5]])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# def CNOT():
|
|
29
|
+
# return Gate(name="CNOT", num_quibits=2, matrix=[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
|
|
30
|
+
def RX(theta):
|
|
31
|
+
return Gate(name="RX", num_qubits=1, params=[theta])
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def RY(theta):
|
|
35
|
+
return Gate(name="RY", num_qubits=1, params=[theta])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def RZ(theta):
|
|
39
|
+
return Gate(name="RZ", num_qubits=1, params=[theta])
|
|
40
|
+
def CNOT():
|
|
41
|
+
return Gate(name="CNOT", num_qubits=2, matrix=[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# You can add more gates as needed
|
|
45
|
+
# For example, CZ, SWAP, Toffoli, etc.
|
|
46
|
+
|
|
47
|
+
def CZ():
|
|
48
|
+
return Gate(
|
|
49
|
+
name="CZ",
|
|
50
|
+
num_qubits=2,
|
|
51
|
+
matrix=[[1, 0, 0, 0], [0, 1, 0, 0],
|
|
52
|
+
[0, 0, 1, 0], [0, 0, 0, -1]])
|
|
53
|
+
def SWAP():
|
|
54
|
+
return Gate(
|
|
55
|
+
name="SWAP",
|
|
56
|
+
num_qubits=2,
|
|
57
|
+
matrix=[[1, 0, 0, 0], [0, 0, 1, 0],
|
|
58
|
+
[0, 1, 0, 0], [0, 0, 0, 1]])
|
|
59
|
+
def Toffoli():
|
|
60
|
+
return Gate(
|
|
61
|
+
name="Toffoli",
|
|
62
|
+
num_qubits=3,
|
|
63
|
+
matrix=[
|
|
64
|
+
[1, 0, 0, 0, 0, 0, 0, 0],
|
|
65
|
+
[0, 1, 0, 0, 0, 0, 0, 0],
|
|
66
|
+
[0, 0, 1, 0, 0, 0, 0, 0],
|
|
67
|
+
[0, 0, 0, 1, 0, 0, 0, 0],
|
|
68
|
+
[0, 0, 0, 0, 1, 0, 0, 0],
|
|
69
|
+
[0, 0, 0, 0, 0, 1, 0, 0],
|
|
70
|
+
[0, 0, 0, 0, 0, 0, 0, 1],
|
|
71
|
+
[0, 0, 0, 0, 0, 0, 1, 0]
|
|
72
|
+
]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|