Skip to content

Commit 92b3bc6

Browse files
jasonhan3weinbe58
andauthored
Copied code from bloqade-circuit to bloqade-gemini (#288)
* refactor: copied code and tests for 'gemini' directories from bloqade-circuit * fix: fixed pyright check errors * refactor: added impls to gemini + moved gemini to bloqade/gemini from bloqade/lanes/gemini * build: bumped bloqade-circuit to 0.14.0 * style: ran formatter * build: used public pypi index * fix: Fixing the pyright issues by changing import style --------- Co-authored-by: Phillip Weinberg <weinbe58@gmail.com>
1 parent 53d61b4 commit 92b3bc6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1637
-381
lines changed

demo/msd.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import math
22

3-
from bloqade.gemini import logical as gemini_logical
4-
from bloqade.gemini.logical.stdlib import default_post_processing
53
from kirin.dialects import ilist
64

75
from bloqade import qubit, squin
6+
from bloqade.gemini import logical as gemini_logical
7+
from bloqade.gemini.logical.stdlib import default_post_processing
88
from bloqade.lanes.logical_mvp import (
99
compile_to_stim_program,
1010
)

demo/pipeline_demo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from bloqade.gemini import logical as gemini_logical
21
from kirin.dialects import ilist
32

43
from bloqade import squin
4+
from bloqade.gemini import logical as gemini_logical
55
from bloqade.lanes.logical_mvp import (
66
compile_to_stim_program,
77
)

demo/pipeline_details.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
# to start we create a simple squin kernel that uses the gemini logical dialect to define a
1010
# terminal measurement on the circuit.
1111

12-
from bloqade.gemini import logical as gemini_logical
13-
1412
# %%
1513
from bloqade import squin
14+
from bloqade.gemini import logical as gemini_logical
1615
from bloqade.lanes.heuristics.logical_placement import LogicalPlacementStrategy
1716

1817

demo/simulator_device_demo.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
import numpy as np
55
from bloqade.decoders import BpLsdDecoder
6-
from bloqade.gemini import logical
7-
from bloqade.gemini.logical.stdlib import default_post_processing
86
from kirin.dialects import ilist
97

108
from bloqade import qubit, squin
9+
from bloqade.gemini import logical
10+
from bloqade.gemini.logical.stdlib import default_post_processing
1111
from bloqade.lanes import GeminiLogicalSimulator
1212

1313

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ authors = [
1111
{ name = "Phillip Weinberg", email = "pweinberg@quera.com" }
1212
]
1313
dependencies = [
14-
"bloqade-circuit[cirq, qasm2, tsim]~=0.13.0",
14+
"bloqade-circuit[cirq, qasm2, tsim]~=0.14.0",
1515
"bloqade-geometry~=0.5.0",
1616
"deprecated>=1.3.1",
1717
"kirin-toolchain~=0.22.6",

python/bloqade/gemini/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import logical as logical

python/bloqade/gemini/analysis/__init__.py

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import analysis as analysis, impls as impls # NOTE: register methods
2+
from .analysis import GeminiLogicalValidation as GeminiLogicalValidation
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from dataclasses import dataclass, field
2+
from typing import Any, ClassVar
3+
4+
from bloqade.analysis.address import Address, AddressAnalysis, AddressReg
5+
from kirin import ir
6+
from kirin.analysis import Forward, ForwardFrame
7+
from kirin.interp import InterpreterError
8+
from kirin.lattice import EmptyLattice
9+
from kirin.validation import ValidationPass
10+
11+
from bloqade import squin
12+
13+
14+
@dataclass
15+
class _GeminiLogicalValidationAnalysis(Forward[EmptyLattice]):
16+
keys = ("gemini.validate.logical",)
17+
18+
lattice = EmptyLattice
19+
addr_frame: ForwardFrame[Address]
20+
21+
first_gates: dict[int, bool] = field(init=False, default_factory=dict)
22+
23+
max_qubits: ClassVar[int] = 10
24+
25+
def eval_fallback(self, frame: ForwardFrame, node: ir.Statement):
26+
if isinstance(node, squin.gate.stmts.Gate):
27+
raise InterpreterError(f"Missing implementation for gate {node}")
28+
29+
return tuple(self.lattice.bottom() for _ in range(len(node.results)))
30+
31+
def check_first_gate(self, qubits: ir.SSAValue) -> bool:
32+
address = self.addr_frame.get(qubits)
33+
34+
if not isinstance(address, AddressReg):
35+
# NOTE: we should have a flat kernel with simple address analysis, so in case we don't
36+
# get concrete addresses, we might as well error here since something's wrong
37+
return False
38+
39+
is_first = True
40+
for addr_int in address.data:
41+
is_first = is_first and self.first_gates.get(addr_int, True)
42+
self.first_gates[addr_int] = False
43+
44+
return is_first
45+
46+
def method_self(self, method: ir.Method) -> EmptyLattice:
47+
return self.lattice.bottom()
48+
49+
50+
@dataclass
51+
class GeminiLogicalValidation(ValidationPass):
52+
"""Validates a logical gemini program"""
53+
54+
def name(self) -> str:
55+
return "Gemini Logical Validation"
56+
57+
def run(self, method: ir.Method) -> tuple[Any, list[ir.ValidationError]]:
58+
addr_frame, _ = AddressAnalysis(method.dialects).run(method)
59+
analysis = _GeminiLogicalValidationAnalysis(
60+
method.dialects, addr_frame=addr_frame
61+
)
62+
frame, _ = analysis.run(method)
63+
64+
return frame, analysis.get_validation_errors()
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
from bloqade.analysis import address
2+
from bloqade.squin import gate
3+
from kirin import interp as _interp, ir
4+
from kirin.analysis import ForwardFrame, const
5+
from kirin.dialects import func, scf
6+
7+
from bloqade import qubit
8+
9+
from .analysis import _GeminiLogicalValidationAnalysis
10+
11+
12+
@scf.dialect.register(key="gemini.validate.logical")
13+
class __ScfGeminiLogicalValidation(_interp.MethodTable):
14+
@_interp.impl(scf.IfElse)
15+
def if_else(
16+
self,
17+
interp: _GeminiLogicalValidationAnalysis,
18+
frame: ForwardFrame,
19+
stmt: scf.IfElse,
20+
):
21+
interp.add_validation_error(
22+
stmt,
23+
ir.ValidationError(
24+
stmt, "If statements are not supported in logical Gemini programs!"
25+
),
26+
)
27+
return (interp.lattice.bottom(),)
28+
29+
@_interp.impl(scf.For)
30+
def for_loop(
31+
self,
32+
interp: _GeminiLogicalValidationAnalysis,
33+
frame: ForwardFrame,
34+
stmt: scf.For,
35+
):
36+
if not isinstance(stmt.iterable.hints.get("const"), const.Value):
37+
interp.add_validation_error(
38+
stmt,
39+
ir.ValidationError(
40+
stmt,
41+
"Non-constant iterable in for loop is not supported in Gemini logical programs!",
42+
),
43+
)
44+
45+
return (interp.lattice.bottom(),)
46+
47+
48+
@func.dialect.register(key="gemini.validate.logical")
49+
class __FuncGeminiLogicalValidation(_interp.MethodTable):
50+
@_interp.impl(func.Invoke)
51+
def invoke(
52+
self,
53+
interp: _GeminiLogicalValidationAnalysis,
54+
frame: ForwardFrame,
55+
stmt: func.Invoke,
56+
):
57+
interp.add_validation_error(
58+
stmt,
59+
ir.ValidationError(
60+
stmt,
61+
"Function invocations not supported in logical Gemini program!",
62+
help="Make sure to decorate your function with `@logical(inline = True)` or `@logical(aggressive_unroll = True)` to inline function calls",
63+
),
64+
)
65+
66+
return tuple(interp.lattice.bottom() for _ in stmt.results)
67+
68+
69+
@gate.dialect.register(key="gemini.validate.logical")
70+
class __GateGeminiLogicalValidation(_interp.MethodTable):
71+
@_interp.impl(gate.stmts.U3)
72+
@_interp.impl(gate.stmts.T)
73+
@_interp.impl(gate.stmts.Rx)
74+
@_interp.impl(gate.stmts.Ry)
75+
@_interp.impl(gate.stmts.Rz)
76+
def non_clifford(
77+
self,
78+
interp: _GeminiLogicalValidationAnalysis,
79+
frame: ForwardFrame,
80+
stmt: gate.stmts.SingleQubitGate | gate.stmts.RotationGate,
81+
):
82+
if interp.check_first_gate(stmt.qubits):
83+
return ()
84+
85+
interp.add_validation_error(
86+
stmt,
87+
ir.ValidationError(
88+
stmt,
89+
f"Non-clifford gate {stmt.name} can only be used for initial state preparation, i.e. as the first gate!",
90+
),
91+
)
92+
return ()
93+
94+
@_interp.impl(gate.stmts.X)
95+
@_interp.impl(gate.stmts.Y)
96+
@_interp.impl(gate.stmts.SqrtX)
97+
@_interp.impl(gate.stmts.SqrtY)
98+
@_interp.impl(gate.stmts.Z)
99+
@_interp.impl(gate.stmts.H)
100+
@_interp.impl(gate.stmts.S)
101+
def clifford(
102+
self,
103+
interp: _GeminiLogicalValidationAnalysis,
104+
frame: ForwardFrame,
105+
stmt: gate.stmts.SingleQubitGate,
106+
):
107+
# NOTE: ignore result, but make sure the first gate flag is set to False
108+
interp.check_first_gate(stmt.qubits)
109+
110+
return ()
111+
112+
@_interp.impl(gate.stmts.CX)
113+
@_interp.impl(gate.stmts.CY)
114+
@_interp.impl(gate.stmts.CZ)
115+
def controlled_gate(
116+
self,
117+
interp: _GeminiLogicalValidationAnalysis,
118+
frame: ForwardFrame,
119+
stmt: gate.stmts.ControlledGate,
120+
):
121+
interp.check_first_gate(stmt.controls)
122+
interp.check_first_gate(stmt.targets)
123+
return ()
124+
125+
126+
@qubit.dialect.register(key="gemini.validate.logical")
127+
class QubitMethods(_interp.MethodTable):
128+
@_interp.impl(qubit.stmts.New)
129+
def check_qubit_allocation(
130+
self,
131+
interp: _GeminiLogicalValidationAnalysis,
132+
frame: ForwardFrame,
133+
stmt: qubit.stmts.New,
134+
):
135+
qubit_val = interp.addr_frame.get(stmt.result)
136+
if not isinstance(qubit_val, address.AddressQubit):
137+
interp.add_validation_error(
138+
stmt,
139+
ir.ValidationError(
140+
stmt,
141+
"Cannot determine qubit address location.",
142+
),
143+
)
144+
return (interp.lattice.bottom(),)
145+
146+
if qubit_val.data >= interp.max_qubits:
147+
interp.add_validation_error(
148+
stmt,
149+
ir.ValidationError(
150+
stmt,
151+
f"Qubit allocations exceeded {interp.max_qubits}.",
152+
),
153+
)
154+
155+
return (interp.lattice.bottom(),)

0 commit comments

Comments
 (0)