Skip to content

Commit efc8546

Browse files
committed
Fidelity analysis for for loops
1 parent cda6a50 commit efc8546

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

src/bloqade/analysis/fidelity/impls.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from kirin import interp
22
from kirin.lattice import EmptyLattice
3-
from kirin.dialects.scf import dialect as scf
4-
from kirin.dialects.scf.stmts import IfElse
3+
from kirin.analysis import const
4+
from kirin.dialects import scf
5+
from kirin.dialects.scf.stmts import For, Yield, IfElse
56

67
from .analysis import FidelityAnalysis
78

89

9-
@scf.register(key="circuit.fidelity")
10+
@scf.dialect.register(key="circuit.fidelity")
1011
class ScfFidelityMethodTable(interp.MethodTable):
1112

1213
@interp.impl(IfElse)
@@ -44,3 +45,40 @@ def if_else(
4445
interp.current_fidelity = then_fidelity
4546
else:
4647
interp.current_fidelity = else_fidelity
48+
49+
@interp.impl(Yield)
50+
def yield_(
51+
self, interp: FidelityAnalysis, frame: interp.Frame[EmptyLattice], stmt: Yield
52+
):
53+
# NOTE: yield can by definition only contain values, never any stmts, so fidelity cannot decrease
54+
return
55+
56+
@interp.impl(For)
57+
def for_loop(
58+
self, interp: FidelityAnalysis, frame: interp.Frame[EmptyLattice], stmt: For
59+
):
60+
61+
if not isinstance(hint := stmt.iterable.hints.get("const"), const.Value):
62+
# NOTE: not clear how long this loop is
63+
# TODO: should we at least count the body once?
64+
return
65+
66+
current_fidelity = interp.current_fidelity
67+
68+
# NOTE: we reset the interpreter fidelity and evaluate the fidelity for the body only once
69+
interp.current_fidelity = 1.0
70+
for s in stmt.body.stmts():
71+
stmt_impl = interp.lookup_registry(frame=frame, stmt=s)
72+
if stmt_impl is None:
73+
continue
74+
75+
stmt_impl(interp=interp, frame=frame, stmt=s)
76+
77+
# NOTE: reset current fidelity now in case of 0 iterations
78+
loop_body_fidelity = interp.current_fidelity
79+
interp.current_fidelity = current_fidelity
80+
81+
# NOTE: now we simply decrease the fidelity according to the number of iterations
82+
iterable = hint.data
83+
for _ in iterable:
84+
interp.current_fidelity *= loop_body_fidelity

test/analysis/fidelity/test_fidelity.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,65 @@ def main_if():
106106
fid_if_analysis.run_analysis(main_if, no_raise=False)
107107

108108
assert 0 < fid_if_analysis.global_fidelity == fid_analysis.global_fidelity < 1
109+
110+
111+
def test_for():
112+
113+
@noise_main
114+
def main():
115+
q = qasm2.qreg(1)
116+
c = qasm2.creg(1)
117+
qasm2.h(q[0])
118+
qasm2.measure(q, c)
119+
120+
# unrolled for loop
121+
qasm2.x(q[0])
122+
qasm2.x(q[0])
123+
qasm2.x(q[0])
124+
125+
qasm2.measure(q, c)
126+
127+
return c
128+
129+
@noise_main
130+
def main_for():
131+
q = qasm2.qreg(1)
132+
c = qasm2.creg(1)
133+
qasm2.h(q[0])
134+
qasm2.measure(q, c)
135+
136+
for _ in range(3):
137+
qasm2.x(q[0])
138+
139+
qasm2.measure(q, c)
140+
return c
141+
142+
px = 0.01
143+
py = 0.01
144+
pz = 0.01
145+
p_loss = 0.01
146+
147+
noise_params = native.GateNoiseParams(
148+
global_loss_prob=p_loss,
149+
global_px=px,
150+
global_py=py,
151+
global_pz=pz,
152+
local_px=0.002,
153+
)
154+
155+
model = NoiseTestModel()
156+
NoisePass(main.dialects, noise_model=model, gate_noise_params=noise_params)(main)
157+
fid_analysis = FidelityAnalysis(main.dialects)
158+
fid_analysis.run_analysis(main, no_raise=False)
159+
160+
model = NoiseTestModel()
161+
NoisePass(main_for.dialects, noise_model=model, gate_noise_params=noise_params)(
162+
main_for
163+
)
164+
165+
main_for.print()
166+
167+
fid_for_analysis = FidelityAnalysis(main_for.dialects)
168+
fid_for_analysis.run_analysis(main_for, no_raise=False)
169+
170+
assert 0 < fid_for_analysis.global_fidelity == fid_analysis.global_fidelity < 1

0 commit comments

Comments
 (0)