Skip to content

Commit 944c31e

Browse files
authored
Merge branch 'main' into david/229-pyqrack-qubit-reset
2 parents 16170e7 + db36429 commit 944c31e

File tree

19 files changed

+186
-39
lines changed

19 files changed

+186
-39
lines changed

src/bloqade/analysis/fidelity/analysis.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,47 @@
1313
class FidelityAnalysis(Forward):
1414
"""
1515
This analysis pass can be used to track the global addresses of qubits and wires.
16+
17+
## Usage examples
18+
19+
```
20+
from bloqade import qasm2
21+
from bloqade.noise import native
22+
from bloqade.analysis.fidelity import FidelityAnalysis
23+
from bloqade.qasm2.passes.noise import NoisePass
24+
25+
noise_main = qasm2.extended.add(native.dialect)
26+
27+
@noise_main
28+
def main():
29+
q = qasm2.qreg(2)
30+
qasm2.x(q[0])
31+
return q
32+
33+
NoisePass(main.dialects)(main)
34+
35+
fid_analysis = FidelityAnalysis(main.dialects)
36+
fid_analysis.run_analysis(main, no_raise=False)
37+
38+
gate_fidelity = fid_analysis.gate_fidelity
39+
atom_survival_probs = fid_analysis.atom_survival_probability
40+
```
1641
"""
1742

1843
keys = ["circuit.fidelity"]
1944
lattice = EmptyLattice
2045

46+
gate_fidelity: float = 1.0
2147
"""
2248
The fidelity of the gate set described by the analysed program. It reduces whenever a noise channel is encountered.
2349
"""
24-
gate_fidelity: float = 1.0
2550

2651
_current_gate_fidelity: float = field(init=False)
2752

53+
atom_survival_probability: list[float] = field(init=False)
2854
"""
2955
The probabilities that each of the atoms in the register survive the duration of the analysed program. The order of the list follows the order they are in the register.
3056
"""
31-
atom_survival_probability: list[float] = field(init=False)
3257

3358
_current_atom_survival_probability: list[float] = field(init=False)
3459

src/bloqade/pyqrack/device.py

Lines changed: 107 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525

2626
@dataclass
2727
class PyQrackSimulatorBase(AbstractSimulatorDevice[PyQrackSimulatorTask]):
28+
"""PyQrack simulation device base class."""
29+
2830
options: PyQrackOptions = field(default_factory=_default_pyqrack_args)
31+
"""options (PyQrackOptions): options passed into the pyqrack simulator."""
32+
2933
loss_m_result: Measurement = field(default=Measurement.One, kw_only=True)
3034
rng_state: np.random.Generator = field(
3135
default_factory=np.random.default_rng, kw_only=True
@@ -99,7 +103,43 @@ def pauli_expectation(pauli: list[Pauli], qubits: list[PyQrackQubit]) -> float:
99103

100104
@dataclass
101105
class StackMemorySimulator(PyQrackSimulatorBase):
102-
"""PyQrack simulator device with precalculated stack of qubits."""
106+
"""
107+
PyQrack simulator device with preallocated stack of qubits.
108+
109+
This can be used to simulate kernels where the number of qubits is known
110+
ahead of time.
111+
112+
## Usage examples
113+
114+
```
115+
# Define a kernel
116+
@qasm2.main
117+
def main():
118+
q = qasm2.qreg(2)
119+
c = qasm2.creg(2)
120+
121+
qasm2.h(q[0])
122+
qasm2.cx(q[0], q[1])
123+
124+
qasm2.measure(q, c)
125+
return q
126+
127+
# Create the simulator object
128+
sim = StackMemorySimulator(min_qubits=2)
129+
130+
# Execute the kernel
131+
qubits = sim.run(main)
132+
```
133+
134+
You can also obtain other information from it, such as the state vector:
135+
136+
```
137+
ket = sim.state_vector(main)
138+
139+
from pyqrack.pauli import Pauli
140+
expectation_vals = sim.pauli_expectation([Pauli.PauliX, Pauli.PauliI], qubits)
141+
```
142+
"""
103143

104144
min_qubits: int = field(default=0, kw_only=True)
105145

@@ -109,6 +149,20 @@ def task(
109149
args: tuple[Any, ...] = (),
110150
kwargs: dict[str, Any] | None = None,
111151
):
152+
"""
153+
Args:
154+
kernel (ir.Method):
155+
The kernel method to run.
156+
args (tuple[Any, ...]):
157+
Positional arguments to pass to the kernel method.
158+
kwargs (dict[str, Any] | None):
159+
Keyword arguments to pass to the kernel method.
160+
161+
Returns:
162+
PyQrackSimulatorTask:
163+
The task object used to track execution.
164+
165+
"""
112166
if kwargs is None:
113167
kwargs = {}
114168

@@ -134,31 +188,67 @@ def task(
134188

135189
@dataclass
136190
class DynamicMemorySimulator(PyQrackSimulatorBase):
137-
"""PyQrack simulator device with dynamic qubit allocation."""
191+
"""
192+
193+
PyQrack simulator device with dynamic qubit allocation.
194+
195+
This can be used to simulate kernels where the number of qubits is not known
196+
ahead of time.
197+
198+
## Usage examples
199+
200+
```
201+
# Define a kernel
202+
@qasm2.main
203+
def main():
204+
q = qasm2.qreg(2)
205+
c = qasm2.creg(2)
206+
207+
qasm2.h(q[0])
208+
qasm2.cx(q[0], q[1])
209+
210+
qasm2.measure(q, c)
211+
return q
212+
213+
# Create the simulator object
214+
sim = DynamicMemorySimulator()
215+
216+
# Execute the kernel
217+
qubits = sim.run(main)
218+
```
219+
220+
You can also obtain other information from it, such as the state vector:
221+
222+
```
223+
ket = sim.state_vector(main)
224+
225+
from pyqrack.pauli import Pauli
226+
expectation_vals = sim.pauli_expectation([Pauli.PauliX, Pauli.PauliI], qubits)
227+
228+
"""
138229

139230
def task(
140231
self,
141232
kernel: ir.Method[Params, RetType],
142233
args: tuple[Any, ...] = (),
143234
kwargs: dict[str, Any] | None = None,
144235
):
236+
"""
237+
Args:
238+
kernel (ir.Method):
239+
The kernel method to run.
240+
args (tuple[Any, ...]):
241+
Positional arguments to pass to the kernel method.
242+
kwargs (dict[str, Any] | None):
243+
Keyword arguments to pass to the kernel method.
244+
245+
Returns:
246+
PyQrackSimulatorTask:
247+
The task object used to track execution.
248+
249+
"""
145250
if kwargs is None:
146251
kwargs = {}
147252

148253
memory = DynamicMemory(self.options.copy())
149254
return self.new_task(kernel, args, kwargs, memory)
150-
151-
152-
def test():
153-
from bloqade.qasm2 import extended
154-
155-
@extended
156-
def main():
157-
return 1
158-
159-
@extended
160-
def obs(result: int) -> int:
161-
return result
162-
163-
res = DynamicMemorySimulator().task(main)
164-
return res.run()

src/bloqade/qasm2/passes/noise.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,34 @@ class NoisePass(Pass):
2121
NOTE: This pass is not guaranteed to be supported long-term in bloqade. We will be
2222
moving towards a more general approach to noise modeling in the future.
2323
24+
## Usage examples
25+
26+
```
27+
from bloqade import qasm2
28+
from bloqade.noise import native
29+
from bloqade.qasm2.passes.noise import NoisePass
30+
31+
noise_main = qasm2.extended.add(native.dialect)
32+
33+
@noise_main
34+
def main():
35+
q = qasm2.qreg(2)
36+
qasm2.h(q[0])
37+
qasm2.cx(q[0], q[1])
38+
return q
39+
40+
# simple IR without any nosie
41+
main.print()
42+
43+
noise_pass = NoisePass(noise_main)
44+
45+
# rewrite stuff in-place
46+
noise_pass.unsafe_run(main)
47+
48+
# now, we do have noise channels in the IR
49+
main.print()
50+
```
51+
2452
"""
2553

2654
noise_model: native.MoveNoiseModelABC = field(

src/bloqade/stim/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .groups import main as main
22
from ._wrappers import * # noqa: F403
3-
from .dialects.aux import * # noqa F403
43
from .dialects.gate import * # noqa F403
54
from .dialects.noise import * # noqa F403
65
from .dialects.collapse import * # noqa F403
6+
from .dialects.auxiliary import * # noqa F403

src/bloqade/stim/_wrappers.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from kirin.lowering import wraps
44

5-
from .dialects import aux, gate, noise, collapse
5+
from .dialects import gate, noise, collapse, auxiliary
66

77

88
# dialect:: gate
@@ -69,32 +69,34 @@ def cz(
6969

7070
## pp
7171
@wraps(gate.SPP)
72-
def spp(targets: tuple[aux.PauliString, ...], dagger=False) -> None: ...
72+
def spp(targets: tuple[auxiliary.PauliString, ...], dagger=False) -> None: ...
7373

7474

7575
# dialect:: aux
76-
@wraps(aux.GetRecord)
77-
def rec(id: int) -> aux.RecordResult: ...
76+
@wraps(auxiliary.GetRecord)
77+
def rec(id: int) -> auxiliary.RecordResult: ...
7878

7979

80-
@wraps(aux.Detector)
80+
@wraps(auxiliary.Detector)
8181
def detector(
82-
coord: tuple[Union[int, float], ...], targets: tuple[aux.RecordResult, ...]
82+
coord: tuple[Union[int, float], ...], targets: tuple[auxiliary.RecordResult, ...]
8383
) -> None: ...
8484

8585

86-
@wraps(aux.ObservableInclude)
87-
def observable_include(idx: int, targets: tuple[aux.RecordResult, ...]) -> None: ...
86+
@wraps(auxiliary.ObservableInclude)
87+
def observable_include(
88+
idx: int, targets: tuple[auxiliary.RecordResult, ...]
89+
) -> None: ...
8890

8991

90-
@wraps(aux.Tick)
92+
@wraps(auxiliary.Tick)
9193
def tick() -> None: ...
9294

9395

94-
@wraps(aux.NewPauliString)
96+
@wraps(auxiliary.NewPauliString)
9597
def pauli_string(
9698
string: tuple[str, ...], flipped: tuple[bool, ...], targets: tuple[int, ...]
97-
) -> aux.PauliString: ...
99+
) -> auxiliary.PauliString: ...
98100

99101

100102
# dialect:: collapse
@@ -123,7 +125,7 @@ def mxx(p: float, targets: tuple[int, ...]) -> None: ...
123125

124126

125127
@wraps(collapse.PPMeasurement)
126-
def mpp(p: float, targets: tuple[aux.PauliString, ...]) -> None: ...
128+
def mpp(p: float, targets: tuple[auxiliary.PauliString, ...]) -> None: ...
127129

128130

129131
@wraps(collapse.RZ)
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from . import aux as aux, gate as gate, noise as noise, collapse as collapse
2-
from .aux.stmts import * # noqa F403
1+
from . import gate as gate, noise as noise, collapse as collapse, auxiliary as auxiliary
32
from .gate.stmts import * # noqa F403
43
from .noise.stmts import * # noqa F403
54
from .collapse.stmts import * # noqa F403
5+
from .auxiliary.stmts import * # noqa F403
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)