bloqade-circuit 0.7.13__py3-none-any.whl → 0.8.0__py3-none-any.whl
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.
Potentially problematic release.
This version of bloqade-circuit might be problematic. Click here for more details.
- bloqade/analysis/address/__init__.py +8 -4
- bloqade/analysis/address/analysis.py +119 -29
- bloqade/analysis/address/impls.py +290 -87
- bloqade/analysis/address/lattice.py +209 -24
- bloqade/analysis/fidelity/analysis.py +2 -2
- bloqade/analysis/measure_id/impls.py +3 -27
- bloqade/cirq_utils/__init__.py +3 -1
- bloqade/cirq_utils/emit/__init__.py +3 -0
- bloqade/cirq_utils/emit/base.py +243 -0
- bloqade/cirq_utils/emit/gate.py +104 -0
- bloqade/cirq_utils/emit/noise.py +90 -0
- bloqade/cirq_utils/emit/qubit.py +35 -0
- bloqade/cirq_utils/lowering.py +664 -0
- bloqade/native/__init__.py +0 -1
- bloqade/native/_prelude.py +3 -3
- bloqade/native/dialects/gate/__init__.py +2 -0
- bloqade/native/dialects/gate/_dialect.py +3 -0
- bloqade/native/dialects/{gates → gate}/_interface.py +5 -5
- bloqade/native/dialects/{gates → gate}/stmts.py +5 -5
- bloqade/native/stdlib/broadcast.py +19 -19
- bloqade/native/stdlib/simple.py +14 -13
- bloqade/native/upstream/__init__.py +5 -0
- bloqade/native/upstream/squin2native.py +136 -0
- bloqade/pyqrack/__init__.py +1 -2
- bloqade/pyqrack/device.py +6 -17
- bloqade/pyqrack/native.py +17 -17
- bloqade/pyqrack/reg.py +1 -6
- bloqade/pyqrack/squin/gate/__init__.py +1 -0
- bloqade/pyqrack/squin/gate/gate.py +136 -0
- bloqade/pyqrack/squin/noise/native.py +120 -54
- bloqade/pyqrack/squin/qubit.py +25 -41
- bloqade/pyqrack/target.py +2 -2
- bloqade/qasm2/dialects/core/address.py +21 -12
- bloqade/qasm2/dialects/noise/fidelity.py +2 -6
- bloqade/qasm2/dialects/noise/model.py +2 -1
- bloqade/qasm2/passes/parallel.py +3 -1
- bloqade/qasm2/rewrite/__init__.py +0 -1
- bloqade/qasm2/rewrite/noise/heuristic_noise.py +7 -17
- bloqade/qasm2/rewrite/parallel_to_glob.py +28 -15
- bloqade/qasm2/rewrite/parallel_to_uop.py +2 -8
- bloqade/qubit/__init__.py +12 -0
- bloqade/qubit/_dialect.py +3 -0
- bloqade/qubit/_interface.py +49 -0
- bloqade/qubit/_prelude.py +45 -0
- bloqade/qubit/analysis/__init__.py +1 -0
- bloqade/qubit/analysis/address_impl.py +40 -0
- bloqade/qubit/stdlib/__init__.py +2 -0
- bloqade/qubit/stdlib/_new.py +34 -0
- bloqade/qubit/stdlib/broadcast.py +62 -0
- bloqade/qubit/stdlib/simple.py +59 -0
- bloqade/qubit/stmts.py +60 -0
- bloqade/rewrite/passes/aggressive_unroll.py +2 -1
- bloqade/squin/__init__.py +44 -17
- bloqade/squin/analysis/__init__.py +0 -1
- bloqade/squin/analysis/schedule.py +2 -2
- bloqade/squin/gate/__init__.py +2 -0
- bloqade/squin/gate/_dialect.py +3 -0
- bloqade/squin/gate/_interface.py +98 -0
- bloqade/squin/gate/stmts.py +119 -0
- bloqade/squin/groups.py +4 -21
- bloqade/squin/noise/__init__.py +1 -9
- bloqade/squin/noise/_dialect.py +1 -1
- bloqade/squin/noise/_interface.py +45 -0
- bloqade/squin/noise/stmts.py +65 -29
- bloqade/squin/rewrite/U3_to_clifford.py +70 -51
- bloqade/squin/rewrite/__init__.py +0 -2
- bloqade/squin/rewrite/remove_dangling_qubits.py +2 -2
- bloqade/squin/rewrite/wrap_analysis.py +4 -35
- bloqade/squin/stdlib/broadcast/__init__.py +34 -0
- bloqade/squin/stdlib/broadcast/_qubit.py +4 -0
- bloqade/squin/stdlib/broadcast/gate.py +260 -0
- bloqade/squin/stdlib/broadcast/noise.py +144 -0
- bloqade/squin/stdlib/simple/__init__.py +33 -0
- bloqade/squin/stdlib/simple/gate.py +242 -0
- bloqade/squin/stdlib/simple/noise.py +126 -0
- bloqade/stim/__init__.py +1 -0
- bloqade/stim/_wrappers.py +6 -0
- bloqade/stim/dialects/noise/emit.py +6 -1
- bloqade/stim/dialects/noise/stmts.py +5 -3
- bloqade/stim/emit/stim_str.py +2 -0
- bloqade/stim/parse/lowering.py +12 -17
- bloqade/stim/passes/__init__.py +0 -1
- bloqade/stim/passes/flatten.py +26 -0
- bloqade/stim/passes/simplify_ifs.py +6 -1
- bloqade/stim/passes/squin_to_stim.py +4 -70
- bloqade/stim/rewrite/__init__.py +0 -4
- bloqade/stim/rewrite/ifs_to_stim.py +23 -29
- bloqade/stim/rewrite/qubit_to_stim.py +96 -51
- bloqade/stim/rewrite/squin_measure.py +9 -18
- bloqade/stim/rewrite/squin_noise.py +132 -108
- bloqade/stim/rewrite/util.py +5 -204
- bloqade/types.py +10 -0
- {bloqade_circuit-0.7.13.dist-info → bloqade_circuit-0.8.0.dist-info}/METADATA +2 -2
- {bloqade_circuit-0.7.13.dist-info → bloqade_circuit-0.8.0.dist-info}/RECORD +96 -100
- bloqade/native/dialects/gates/__init__.py +0 -3
- bloqade/native/dialects/gates/_dialect.py +0 -3
- bloqade/pyqrack/squin/op.py +0 -180
- bloqade/pyqrack/squin/runtime.py +0 -543
- bloqade/pyqrack/squin/wire.py +0 -51
- bloqade/squin/_typeinfer.py +0 -20
- bloqade/squin/analysis/address_impl.py +0 -71
- bloqade/squin/analysis/nsites/__init__.py +0 -9
- bloqade/squin/analysis/nsites/analysis.py +0 -50
- bloqade/squin/analysis/nsites/impls.py +0 -99
- bloqade/squin/analysis/nsites/lattice.py +0 -49
- bloqade/squin/cirq/__init__.py +0 -306
- bloqade/squin/cirq/emit/emit_circuit.py +0 -129
- bloqade/squin/cirq/emit/noise.py +0 -49
- bloqade/squin/cirq/emit/op.py +0 -176
- bloqade/squin/cirq/emit/qubit.py +0 -58
- bloqade/squin/cirq/emit/runtime.py +0 -242
- bloqade/squin/cirq/lowering.py +0 -439
- bloqade/squin/lowering.py +0 -80
- bloqade/squin/noise/_wrapper.py +0 -36
- bloqade/squin/noise/rewrite.py +0 -129
- bloqade/squin/op/__init__.py +0 -41
- bloqade/squin/op/_dialect.py +0 -3
- bloqade/squin/op/_wrapper.py +0 -121
- bloqade/squin/op/number.py +0 -5
- bloqade/squin/op/rewrite.py +0 -46
- bloqade/squin/op/stdlib.py +0 -62
- bloqade/squin/op/stmts.py +0 -300
- bloqade/squin/op/traits.py +0 -43
- bloqade/squin/op/types.py +0 -128
- bloqade/squin/parallel.py +0 -200
- bloqade/squin/qubit.py +0 -194
- bloqade/squin/rewrite/canonicalize.py +0 -60
- bloqade/squin/rewrite/desugar.py +0 -102
- bloqade/squin/stdlib/channel.py +0 -86
- bloqade/squin/stdlib/gate.py +0 -201
- bloqade/squin/types.py +0 -8
- bloqade/squin/wire.py +0 -201
- bloqade/stim/rewrite/wire_identity_elimination.py +0 -24
- bloqade/stim/rewrite/wire_to_stim.py +0 -57
- {bloqade_circuit-0.7.13.dist-info → bloqade_circuit-0.8.0.dist-info}/WHEEL +0 -0
- {bloqade_circuit-0.7.13.dist-info → bloqade_circuit-0.8.0.dist-info}/licenses/LICENSE +0 -0
bloqade/pyqrack/squin/wire.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from kirin import interp
|
|
2
|
-
|
|
3
|
-
from bloqade.squin import wire
|
|
4
|
-
from bloqade.pyqrack.reg import PyQrackWire, PyQrackQubit
|
|
5
|
-
from bloqade.pyqrack.base import PyQrackInterpreter
|
|
6
|
-
|
|
7
|
-
from .runtime import OperatorRuntimeABC
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@wire.dialect.register(key="pyqrack")
|
|
11
|
-
class PyQrackMethods(interp.MethodTable):
|
|
12
|
-
# @interp.impl(wire.Wrap)
|
|
13
|
-
# def wrap(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Wrap):
|
|
14
|
-
# traits = frozenset({lowering.FromPythonCall(), WireTerminator()})
|
|
15
|
-
# wire: ir.SSAValue = info.argument(WireType)
|
|
16
|
-
# qubit: ir.SSAValue = info.argument(QubitType)
|
|
17
|
-
|
|
18
|
-
@interp.impl(wire.Unwrap)
|
|
19
|
-
def unwrap(
|
|
20
|
-
self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Unwrap
|
|
21
|
-
):
|
|
22
|
-
q: PyQrackQubit = frame.get(stmt.qubit)
|
|
23
|
-
return (PyQrackWire(q),)
|
|
24
|
-
|
|
25
|
-
@interp.impl(wire.Apply)
|
|
26
|
-
def apply(self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Apply):
|
|
27
|
-
ws = stmt.inputs
|
|
28
|
-
assert isinstance(ws, tuple)
|
|
29
|
-
qubits: list[PyQrackQubit] = []
|
|
30
|
-
for w in ws:
|
|
31
|
-
assert isinstance(w, PyQrackWire)
|
|
32
|
-
qubits.append(w.qubit)
|
|
33
|
-
op: OperatorRuntimeABC = frame.get(stmt.operator)
|
|
34
|
-
|
|
35
|
-
op.apply(*qubits)
|
|
36
|
-
|
|
37
|
-
out_ws = [PyQrackWire(qbit) for qbit in qubits]
|
|
38
|
-
return (out_ws,)
|
|
39
|
-
|
|
40
|
-
@interp.impl(wire.Measure)
|
|
41
|
-
def measure(
|
|
42
|
-
self, interp: PyQrackInterpreter, frame: interp.Frame, stmt: wire.Measure
|
|
43
|
-
):
|
|
44
|
-
w: PyQrackWire = frame.get(stmt.wire)
|
|
45
|
-
qbit = w.qubit
|
|
46
|
-
|
|
47
|
-
if not qbit.is_active():
|
|
48
|
-
return (interp.loss_m_result,)
|
|
49
|
-
|
|
50
|
-
res: bool = bool(qbit.sim_reg.m(qbit.addr))
|
|
51
|
-
return (res,)
|
bloqade/squin/_typeinfer.py
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from kirin import types, interp
|
|
2
|
-
from kirin.analysis import TypeInference, const
|
|
3
|
-
from kirin.dialects import ilist
|
|
4
|
-
|
|
5
|
-
from bloqade import squin
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@squin.qubit.dialect.register(key="typeinfer")
|
|
9
|
-
class TypeInfer(interp.MethodTable):
|
|
10
|
-
@interp.impl(squin.qubit.New)
|
|
11
|
-
def _call(self, interp: TypeInference, frame: interp.Frame, stmt: squin.qubit.New):
|
|
12
|
-
# based on Xiu-zhe (Roger) Luo's get_const_value function
|
|
13
|
-
|
|
14
|
-
if (hint := stmt.n_qubits.hints.get("const")) is None:
|
|
15
|
-
return (ilist.IListType[squin.qubit.QubitType, types.Any],)
|
|
16
|
-
|
|
17
|
-
if isinstance(hint, const.Value) and isinstance(hint.data, int):
|
|
18
|
-
return (ilist.IListType[squin.qubit.QubitType, types.Literal(hint.data)],)
|
|
19
|
-
|
|
20
|
-
return (ilist.IListType[squin.qubit.QubitType, types.Any],)
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
from kirin import interp
|
|
2
|
-
from kirin.analysis import ForwardFrame
|
|
3
|
-
|
|
4
|
-
from bloqade.analysis.address.lattice import (
|
|
5
|
-
Address,
|
|
6
|
-
AddressReg,
|
|
7
|
-
AddressWire,
|
|
8
|
-
AddressQubit,
|
|
9
|
-
)
|
|
10
|
-
from bloqade.analysis.address.analysis import AddressAnalysis
|
|
11
|
-
|
|
12
|
-
from .. import wire, qubit
|
|
13
|
-
|
|
14
|
-
# Address lattice elements we can work with:
|
|
15
|
-
## NotQubit (bottom), AnyAddress (top)
|
|
16
|
-
|
|
17
|
-
## AddressTuple -> data: tuple[Address, ...]
|
|
18
|
-
### Recursive type, could contain itself or other variants
|
|
19
|
-
### This pops up in cases where you can have an IList/Tuple
|
|
20
|
-
### That contains elements that could be other Address types
|
|
21
|
-
|
|
22
|
-
## AddressReg -> data: Sequence[int]
|
|
23
|
-
### specific to creation of a register of qubits
|
|
24
|
-
|
|
25
|
-
## AddressQubit -> data: int
|
|
26
|
-
### Base qubit address type
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@wire.dialect.register(key="qubit.address")
|
|
30
|
-
class SquinWireMethodTable(interp.MethodTable):
|
|
31
|
-
|
|
32
|
-
@interp.impl(wire.Unwrap)
|
|
33
|
-
def unwrap(
|
|
34
|
-
self,
|
|
35
|
-
interp_: AddressAnalysis,
|
|
36
|
-
frame: ForwardFrame[Address],
|
|
37
|
-
stmt: wire.Unwrap,
|
|
38
|
-
):
|
|
39
|
-
|
|
40
|
-
origin_qubit = frame.get(stmt.qubit)
|
|
41
|
-
|
|
42
|
-
if isinstance(origin_qubit, AddressQubit):
|
|
43
|
-
return (AddressWire(origin_qubit=origin_qubit),)
|
|
44
|
-
else:
|
|
45
|
-
return (Address.top(),)
|
|
46
|
-
|
|
47
|
-
@interp.impl(wire.Apply)
|
|
48
|
-
def apply(
|
|
49
|
-
self,
|
|
50
|
-
interp_: AddressAnalysis,
|
|
51
|
-
frame: ForwardFrame[Address],
|
|
52
|
-
stmt: wire.Apply,
|
|
53
|
-
):
|
|
54
|
-
return frame.get_values(stmt.inputs)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@qubit.dialect.register(key="qubit.address")
|
|
58
|
-
class SquinQubitMethodTable(interp.MethodTable):
|
|
59
|
-
|
|
60
|
-
# This can be treated like a QRegNew impl
|
|
61
|
-
@interp.impl(qubit.New)
|
|
62
|
-
def new(
|
|
63
|
-
self,
|
|
64
|
-
interp_: AddressAnalysis,
|
|
65
|
-
frame: ForwardFrame[Address],
|
|
66
|
-
stmt: qubit.New,
|
|
67
|
-
):
|
|
68
|
-
n_qubits = interp_.get_const_value(int, stmt.n_qubits)
|
|
69
|
-
addr = AddressReg(range(interp_.next_address, interp_.next_address + n_qubits))
|
|
70
|
-
interp_.next_address += n_qubits
|
|
71
|
-
return (addr,)
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# from typing import cast
|
|
2
|
-
|
|
3
|
-
from kirin import ir
|
|
4
|
-
from kirin.analysis import Forward
|
|
5
|
-
from kirin.analysis.forward import ForwardFrame
|
|
6
|
-
|
|
7
|
-
from bloqade.squin.op.types import OpType
|
|
8
|
-
from bloqade.squin.op.traits import HasSites, FixedSites
|
|
9
|
-
|
|
10
|
-
from .lattice import Sites, NoSites, NumberSites
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class NSitesAnalysis(Forward[Sites]):
|
|
14
|
-
|
|
15
|
-
keys = ["op.nsites"]
|
|
16
|
-
lattice = Sites
|
|
17
|
-
|
|
18
|
-
# Take a page from how constprop works in Kirin
|
|
19
|
-
|
|
20
|
-
## This gets called before the registry look up
|
|
21
|
-
def eval_stmt(self, frame: ForwardFrame, stmt: ir.Statement):
|
|
22
|
-
method = self.lookup_registry(frame, stmt)
|
|
23
|
-
if method is not None:
|
|
24
|
-
return method(self, frame, stmt)
|
|
25
|
-
elif stmt.has_trait(HasSites):
|
|
26
|
-
has_sites_trait = stmt.get_present_trait(HasSites)
|
|
27
|
-
sites = has_sites_trait.get_sites(stmt)
|
|
28
|
-
return (NumberSites(sites=sites),)
|
|
29
|
-
elif stmt.has_trait(FixedSites):
|
|
30
|
-
sites_trait = stmt.get_present_trait(FixedSites)
|
|
31
|
-
return (NumberSites(sites=sites_trait.data),)
|
|
32
|
-
else:
|
|
33
|
-
return (NoSites(),)
|
|
34
|
-
|
|
35
|
-
# For when no implementation is found for the statement
|
|
36
|
-
def eval_stmt_fallback(
|
|
37
|
-
self, frame: ForwardFrame[Sites], stmt: ir.Statement
|
|
38
|
-
) -> tuple[Sites, ...]: # some form of Sites will go back into the frame
|
|
39
|
-
return tuple(
|
|
40
|
-
(
|
|
41
|
-
self.lattice.top()
|
|
42
|
-
if result.type.is_subseteq(OpType)
|
|
43
|
-
else self.lattice.bottom()
|
|
44
|
-
)
|
|
45
|
-
for result in stmt.results
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
def run_method(self, method: ir.Method, args: tuple[Sites, ...]):
|
|
49
|
-
# NOTE: we do not support dynamic calls here, thus no need to propagate method object
|
|
50
|
-
return self.run_callable(method.code, (self.lattice.bottom(),) + args)
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
from kirin import interp
|
|
2
|
-
from kirin.dialects import scf, func
|
|
3
|
-
from kirin.dialects.scf.typeinfer import TypeInfer as ScfTypeInfer
|
|
4
|
-
|
|
5
|
-
from bloqade.squin import op, wire
|
|
6
|
-
|
|
7
|
-
from .lattice import (
|
|
8
|
-
NoSites,
|
|
9
|
-
NumberSites,
|
|
10
|
-
)
|
|
11
|
-
from .analysis import NSitesAnalysis
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@wire.dialect.register(key="op.nsites")
|
|
15
|
-
class SquinWire(interp.MethodTable):
|
|
16
|
-
|
|
17
|
-
@interp.impl(wire.Apply)
|
|
18
|
-
@interp.impl(wire.Broadcast)
|
|
19
|
-
def apply(
|
|
20
|
-
self,
|
|
21
|
-
interp: NSitesAnalysis,
|
|
22
|
-
frame: interp.Frame,
|
|
23
|
-
stmt: wire.Apply | wire.Broadcast,
|
|
24
|
-
):
|
|
25
|
-
|
|
26
|
-
return tuple(frame.get(input) for input in stmt.inputs)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@op.dialect.register(key="op.nsites")
|
|
30
|
-
class SquinOp(interp.MethodTable):
|
|
31
|
-
|
|
32
|
-
@interp.impl(op.stmts.Kron)
|
|
33
|
-
def kron(self, interp: NSitesAnalysis, frame: interp.Frame, stmt: op.stmts.Kron):
|
|
34
|
-
lhs = frame.get(stmt.lhs)
|
|
35
|
-
rhs = frame.get(stmt.rhs)
|
|
36
|
-
if isinstance(lhs, NumberSites) and isinstance(rhs, NumberSites):
|
|
37
|
-
new_n_sites = lhs.sites + rhs.sites
|
|
38
|
-
return (NumberSites(sites=new_n_sites),)
|
|
39
|
-
else:
|
|
40
|
-
return (NoSites(),)
|
|
41
|
-
|
|
42
|
-
@interp.impl(op.stmts.Mult)
|
|
43
|
-
def mult(self, interp: NSitesAnalysis, frame: interp.Frame, stmt: op.stmts.Mult):
|
|
44
|
-
lhs = frame.get(stmt.lhs)
|
|
45
|
-
rhs = frame.get(stmt.rhs)
|
|
46
|
-
|
|
47
|
-
if isinstance(lhs, NumberSites) and isinstance(rhs, NumberSites):
|
|
48
|
-
lhs_sites = lhs.sites
|
|
49
|
-
rhs_sites = rhs.sites
|
|
50
|
-
# I originally considered throwing an exception here
|
|
51
|
-
# but Xiu-zhe (Roger) Luo has pointed out it would be
|
|
52
|
-
# a much better UX to add a type element that
|
|
53
|
-
# could explicitly indicate the error. The downside
|
|
54
|
-
# is you'll have some added complexity in the type lattice.
|
|
55
|
-
if lhs_sites != rhs_sites:
|
|
56
|
-
return (NoSites(),)
|
|
57
|
-
else:
|
|
58
|
-
return (NumberSites(sites=lhs_sites + rhs_sites),)
|
|
59
|
-
else:
|
|
60
|
-
return (NoSites(),)
|
|
61
|
-
|
|
62
|
-
@interp.impl(op.stmts.Control)
|
|
63
|
-
def control(
|
|
64
|
-
self, interp: NSitesAnalysis, frame: interp.Frame, stmt: op.stmts.Control
|
|
65
|
-
):
|
|
66
|
-
op_sites = frame.get(stmt.op)
|
|
67
|
-
|
|
68
|
-
if isinstance(op_sites, NumberSites):
|
|
69
|
-
n_sites = op_sites.sites
|
|
70
|
-
return (NumberSites(sites=n_sites + stmt.n_controls),)
|
|
71
|
-
else:
|
|
72
|
-
return (NoSites(),)
|
|
73
|
-
|
|
74
|
-
@interp.impl(op.stmts.Rot)
|
|
75
|
-
def rot(self, interp: NSitesAnalysis, frame: interp.Frame, stmt: op.stmts.Rot):
|
|
76
|
-
op_sites = frame.get(stmt.axis)
|
|
77
|
-
return (op_sites,)
|
|
78
|
-
|
|
79
|
-
@interp.impl(op.stmts.Scale)
|
|
80
|
-
def scale(self, interp: NSitesAnalysis, frame: interp.Frame, stmt: op.stmts.Scale):
|
|
81
|
-
op_sites = frame.get(stmt.op)
|
|
82
|
-
return (op_sites,)
|
|
83
|
-
|
|
84
|
-
@interp.impl(op.stmts.PauliString)
|
|
85
|
-
def pauli_string(
|
|
86
|
-
self, interp: NSitesAnalysis, frame: interp.Frame, stmt: op.stmts.PauliString
|
|
87
|
-
):
|
|
88
|
-
s = stmt.string
|
|
89
|
-
return (NumberSites(sites=len(s)),)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
@scf.dialect.register(key="op.nsites")
|
|
93
|
-
class ScfSquinOp(ScfTypeInfer):
|
|
94
|
-
pass
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@func.dialect.register(key="op.nsites")
|
|
98
|
-
class FuncSquinOp(func.typeinfer.TypeInfer):
|
|
99
|
-
pass
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
from typing import final
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
from kirin.lattice import (
|
|
5
|
-
SingletonMeta,
|
|
6
|
-
BoundedLattice,
|
|
7
|
-
SimpleJoinMixin,
|
|
8
|
-
SimpleMeetMixin,
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class Sites(
|
|
14
|
-
SimpleJoinMixin["Sites"], SimpleMeetMixin["Sites"], BoundedLattice["Sites"]
|
|
15
|
-
):
|
|
16
|
-
@classmethod
|
|
17
|
-
def bottom(cls) -> "Sites":
|
|
18
|
-
return NoSites()
|
|
19
|
-
|
|
20
|
-
@classmethod
|
|
21
|
-
def top(cls) -> "Sites":
|
|
22
|
-
return AnySites()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@final
|
|
26
|
-
@dataclass
|
|
27
|
-
class NoSites(Sites, metaclass=SingletonMeta):
|
|
28
|
-
|
|
29
|
-
def is_subseteq(self, other: Sites) -> bool:
|
|
30
|
-
return True
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@final
|
|
34
|
-
@dataclass
|
|
35
|
-
class AnySites(Sites, metaclass=SingletonMeta):
|
|
36
|
-
|
|
37
|
-
def is_subseteq(self, other: Sites) -> bool:
|
|
38
|
-
return isinstance(other, Sites)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
@final
|
|
42
|
-
@dataclass
|
|
43
|
-
class NumberSites(Sites):
|
|
44
|
-
sites: int
|
|
45
|
-
|
|
46
|
-
def is_subseteq(self, other: Sites) -> bool:
|
|
47
|
-
if isinstance(other, NumberSites):
|
|
48
|
-
return self.sites == other.sites
|
|
49
|
-
return False
|
bloqade/squin/cirq/__init__.py
DELETED
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
from typing import Any, Sequence
|
|
2
|
-
from warnings import warn
|
|
3
|
-
|
|
4
|
-
import cirq
|
|
5
|
-
from kirin import ir, types
|
|
6
|
-
from kirin.emit import EmitError
|
|
7
|
-
from kirin.dialects import func
|
|
8
|
-
|
|
9
|
-
from . import lowering as lowering
|
|
10
|
-
from .. import kernel
|
|
11
|
-
|
|
12
|
-
# NOTE: just to register methods
|
|
13
|
-
from .emit import op as op, noise as noise, qubit as qubit
|
|
14
|
-
from .lowering import Squin
|
|
15
|
-
from ..noise.rewrite import RewriteNoiseStmts
|
|
16
|
-
from .emit.emit_circuit import EmitCirq
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def load_circuit(
|
|
20
|
-
circuit: cirq.Circuit,
|
|
21
|
-
kernel_name: str = "main",
|
|
22
|
-
dialects: ir.DialectGroup = kernel,
|
|
23
|
-
register_as_argument: bool = False,
|
|
24
|
-
return_register: bool = False,
|
|
25
|
-
register_argument_name: str = "q",
|
|
26
|
-
globals: dict[str, Any] | None = None,
|
|
27
|
-
file: str | None = None,
|
|
28
|
-
lineno_offset: int = 0,
|
|
29
|
-
col_offset: int = 0,
|
|
30
|
-
compactify: bool = True,
|
|
31
|
-
):
|
|
32
|
-
"""Converts a cirq.Circuit object into a squin kernel.
|
|
33
|
-
|
|
34
|
-
Args:
|
|
35
|
-
circuit (cirq.Circuit): The circuit to load.
|
|
36
|
-
|
|
37
|
-
Keyword Args:
|
|
38
|
-
kernel_name (str): The name of the kernel to load. Defaults to "main".
|
|
39
|
-
dialects (ir.DialectGroup | None): The dialects to use. Defaults to `squin.kernel`.
|
|
40
|
-
register_as_argument (bool): Determine whether the resulting kernel function should accept
|
|
41
|
-
a single `ilist.IList[Qubit, Any]` argument that is a list of qubits used within the
|
|
42
|
-
function. This allows you to compose kernel functions generated from circuits.
|
|
43
|
-
Defaults to `False`.
|
|
44
|
-
return_register (bool): Determine whether the resulting kernel functionr returns a
|
|
45
|
-
single value of type `ilist.IList[Qubit, Any]` that is the list of qubits used
|
|
46
|
-
in the kernel function. Useful when you want to compose multiple kernel functions
|
|
47
|
-
generated from circuits. Defaults to `False`.
|
|
48
|
-
register_argument_name (str): The name of the argument that represents the qubit register.
|
|
49
|
-
Only used when `register_as_argument=True`. Defaults to "q".
|
|
50
|
-
globals (dict[str, Any] | None): The global variables to use. Defaults to None.
|
|
51
|
-
file (str | None): The file name for error reporting. Defaults to None.
|
|
52
|
-
lineno_offset (int): The line number offset for error reporting. Defaults to 0.
|
|
53
|
-
col_offset (int): The column number offset for error reporting. Defaults to 0.
|
|
54
|
-
compactify (bool): Whether to compactify the output. Defaults to True.
|
|
55
|
-
|
|
56
|
-
## Usage Examples:
|
|
57
|
-
|
|
58
|
-
```python
|
|
59
|
-
# from cirq's "hello qubit" example
|
|
60
|
-
import cirq
|
|
61
|
-
from bloqade import squin
|
|
62
|
-
|
|
63
|
-
# Pick a qubit.
|
|
64
|
-
qubit = cirq.GridQubit(0, 0)
|
|
65
|
-
|
|
66
|
-
# Create a circuit.
|
|
67
|
-
circuit = cirq.Circuit(
|
|
68
|
-
cirq.X(qubit)**0.5, # Square root of NOT.
|
|
69
|
-
cirq.measure(qubit, key='m') # Measurement.
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
# load the circuit as squin
|
|
73
|
-
main = squin.load_circuit(circuit)
|
|
74
|
-
|
|
75
|
-
# print the resulting IR
|
|
76
|
-
main.print()
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
You can also compose kernel functions generated from circuits by passing in
|
|
80
|
-
and / or returning the respective quantum registers:
|
|
81
|
-
|
|
82
|
-
```python
|
|
83
|
-
q = cirq.LineQubit.range(2)
|
|
84
|
-
circuit = cirq.Circuit(cirq.H(q[0]), cirq.CX(*q))
|
|
85
|
-
|
|
86
|
-
get_entangled_qubits = squin.cirq.load_circuit(
|
|
87
|
-
circuit, return_register=True, kernel_name="get_entangled_qubits"
|
|
88
|
-
)
|
|
89
|
-
get_entangled_qubits.print()
|
|
90
|
-
|
|
91
|
-
entangle_qubits = squin.cirq.load_circuit(
|
|
92
|
-
circuit, register_as_argument=True, kernel_name="entangle_qubits"
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
@squin.kernel
|
|
96
|
-
def main():
|
|
97
|
-
qreg = get_entangled_qubits()
|
|
98
|
-
qreg2 = squin.qubit.new(1)
|
|
99
|
-
entangle_qubits([qreg[1], qreg2[0]])
|
|
100
|
-
return squin.qubit.measure(qreg2)
|
|
101
|
-
```
|
|
102
|
-
"""
|
|
103
|
-
|
|
104
|
-
target = Squin(dialects=dialects, circuit=circuit)
|
|
105
|
-
body = target.run(
|
|
106
|
-
circuit,
|
|
107
|
-
source=str(circuit), # TODO: proper source string
|
|
108
|
-
file=file,
|
|
109
|
-
globals=globals,
|
|
110
|
-
lineno_offset=lineno_offset,
|
|
111
|
-
col_offset=col_offset,
|
|
112
|
-
compactify=compactify,
|
|
113
|
-
register_as_argument=register_as_argument,
|
|
114
|
-
register_argument_name=register_argument_name,
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
if return_register:
|
|
118
|
-
return_value = target.qreg
|
|
119
|
-
else:
|
|
120
|
-
return_value = func.ConstantNone()
|
|
121
|
-
body.blocks[0].stmts.append(return_value)
|
|
122
|
-
|
|
123
|
-
return_node = func.Return(value_or_stmt=return_value)
|
|
124
|
-
body.blocks[0].stmts.append(return_node)
|
|
125
|
-
|
|
126
|
-
self_arg_name = kernel_name + "_self"
|
|
127
|
-
arg_names = [self_arg_name]
|
|
128
|
-
if register_as_argument:
|
|
129
|
-
args = (target.qreg.type,)
|
|
130
|
-
arg_names.append(register_argument_name)
|
|
131
|
-
else:
|
|
132
|
-
args = ()
|
|
133
|
-
|
|
134
|
-
# NOTE: add _self as argument; need to know signature before so do it after lowering
|
|
135
|
-
signature = func.Signature(args, return_node.value.type)
|
|
136
|
-
body.blocks[0].args.insert_from(
|
|
137
|
-
0,
|
|
138
|
-
types.Generic(ir.Method, types.Tuple.where(signature.inputs), signature.output),
|
|
139
|
-
self_arg_name,
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
code = func.Function(
|
|
143
|
-
sym_name=kernel_name,
|
|
144
|
-
signature=signature,
|
|
145
|
-
body=body,
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
return ir.Method(
|
|
149
|
-
mod=None,
|
|
150
|
-
py_func=None,
|
|
151
|
-
sym_name=kernel_name,
|
|
152
|
-
arg_names=arg_names,
|
|
153
|
-
dialects=dialects,
|
|
154
|
-
code=code,
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def emit_circuit(
|
|
159
|
-
mt: ir.Method,
|
|
160
|
-
qubits: Sequence[cirq.Qid] | None = None,
|
|
161
|
-
circuit_qubits: Sequence[cirq.Qid] | None = None,
|
|
162
|
-
args: tuple = (),
|
|
163
|
-
ignore_returns: bool = False,
|
|
164
|
-
) -> cirq.Circuit:
|
|
165
|
-
"""Converts a squin.kernel method to a cirq.Circuit object.
|
|
166
|
-
|
|
167
|
-
Args:
|
|
168
|
-
mt (ir.Method): The kernel method from which to construct the circuit.
|
|
169
|
-
|
|
170
|
-
Keyword Args:
|
|
171
|
-
circuit_qubits (Sequence[cirq.Qid] | None):
|
|
172
|
-
A list of qubits to use as the qubits in the circuit. Defaults to None.
|
|
173
|
-
If this is None, then `cirq.LineQubit`s are inserted for every `squin.qubit.new`
|
|
174
|
-
statement in the order they appear inside the kernel.
|
|
175
|
-
**Note**: If a list of qubits is provided, make sure that there is a sufficient
|
|
176
|
-
number of qubits for the resulting circuit.
|
|
177
|
-
args (tuple):
|
|
178
|
-
The arguments of the kernel function from which to emit a circuit.
|
|
179
|
-
ignore_returns (bool):
|
|
180
|
-
If `False`, emitting a circuit from a kernel that returns a value will error.
|
|
181
|
-
Set it to `True` in order to ignore the return value(s). Defaults to `False`.
|
|
182
|
-
|
|
183
|
-
## Examples:
|
|
184
|
-
|
|
185
|
-
Here's a very basic example:
|
|
186
|
-
|
|
187
|
-
```python
|
|
188
|
-
from bloqade import squin
|
|
189
|
-
|
|
190
|
-
@squin.kernel
|
|
191
|
-
def main():
|
|
192
|
-
q = squin.qubit.new(2)
|
|
193
|
-
h = squin.op.h()
|
|
194
|
-
squin.qubit.apply(h, q[0])
|
|
195
|
-
cx = squin.op.cx()
|
|
196
|
-
squin.qubit.apply(cx, q)
|
|
197
|
-
|
|
198
|
-
circuit = squin.cirq.emit_circuit(main)
|
|
199
|
-
|
|
200
|
-
print(circuit)
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
You can also compose multiple kernels. Those are emitted as subcircuits within the "main" circuit.
|
|
204
|
-
Subkernels can accept arguments and return a value.
|
|
205
|
-
|
|
206
|
-
```python
|
|
207
|
-
from bloqade import squin
|
|
208
|
-
from kirin.dialects import ilist
|
|
209
|
-
from typing import Literal
|
|
210
|
-
import cirq
|
|
211
|
-
|
|
212
|
-
@squin.kernel
|
|
213
|
-
def entangle(q: ilist.IList[squin.qubit.Qubit, Literal[2]]):
|
|
214
|
-
h = squin.op.h()
|
|
215
|
-
squin.qubit.apply(h, q[0])
|
|
216
|
-
cx = squin.op.cx()
|
|
217
|
-
squin.qubit.apply(cx, q)
|
|
218
|
-
return cx
|
|
219
|
-
|
|
220
|
-
@squin.kernel
|
|
221
|
-
def main():
|
|
222
|
-
q = squin.qubit.new(2)
|
|
223
|
-
cx = entangle(q)
|
|
224
|
-
q2 = squin.qubit.new(3)
|
|
225
|
-
squin.qubit.apply(cx, [q[1], q2[2]])
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
# custom list of qubits on grid
|
|
229
|
-
qubits = [cirq.GridQubit(i, i+1) for i in range(5)]
|
|
230
|
-
|
|
231
|
-
circuit = squin.cirq.emit_circuit(main, circuit_qubits=qubits)
|
|
232
|
-
print(circuit)
|
|
233
|
-
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
We also passed in a custom list of qubits above. This allows you to provide a custom geometry
|
|
237
|
-
and manipulate the qubits in other circuits directly written in cirq as well.
|
|
238
|
-
"""
|
|
239
|
-
|
|
240
|
-
if circuit_qubits is None and qubits is not None:
|
|
241
|
-
circuit_qubits = qubits
|
|
242
|
-
warn(
|
|
243
|
-
"The keyword argument `qubits` is deprecated. Use `circuit_qubits` instead."
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
if (
|
|
247
|
-
not ignore_returns
|
|
248
|
-
and isinstance(mt.code, func.Function)
|
|
249
|
-
and not mt.code.signature.output.is_subseteq(types.NoneType)
|
|
250
|
-
):
|
|
251
|
-
raise EmitError(
|
|
252
|
-
"The method you are trying to convert to a circuit has a return value, but returning from a circuit is not supported."
|
|
253
|
-
" Set `ignore_returns = True` in order to simply ignore the return values and emit a circuit."
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
if len(args) != len(mt.args):
|
|
257
|
-
raise ValueError(
|
|
258
|
-
f"The method from which you're trying to emit a circuit takes {len(mt.args)} as input, but you passed in {len(args)} via the `args` keyword!"
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
emitter = EmitCirq(qubits=qubits)
|
|
262
|
-
|
|
263
|
-
# Rewrite noise statements
|
|
264
|
-
mt_ = mt.similar(mt.dialects)
|
|
265
|
-
RewriteNoiseStmts(mt_.dialects)(mt_)
|
|
266
|
-
|
|
267
|
-
return emitter.run(mt_, args=args)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
def dump_circuit(
|
|
271
|
-
mt: ir.Method,
|
|
272
|
-
circuit_qubits: Sequence[cirq.Qid] | None = None,
|
|
273
|
-
args: tuple = (),
|
|
274
|
-
qubits: Sequence[cirq.Qid] | None = None,
|
|
275
|
-
ignore_returns: bool = False,
|
|
276
|
-
**kwargs,
|
|
277
|
-
):
|
|
278
|
-
"""Converts a squin.kernel method to a cirq.Circuit object and dumps it as JSON.
|
|
279
|
-
|
|
280
|
-
This just runs `emit_circuit` and calls the `cirq.to_json` function to emit a JSON.
|
|
281
|
-
|
|
282
|
-
Args:
|
|
283
|
-
mt (ir.Method): The kernel method from which to construct the circuit.
|
|
284
|
-
|
|
285
|
-
Keyword Args:
|
|
286
|
-
circuit_qubits (Sequence[cirq.Qid] | None):
|
|
287
|
-
A list of qubits to use as the qubits in the circuit. Defaults to None.
|
|
288
|
-
If this is None, then `cirq.LineQubit`s are inserted for every `squin.qubit.new`
|
|
289
|
-
statement in the order they appear inside the kernel.
|
|
290
|
-
**Note**: If a list of qubits is provided, make sure that there is a sufficient
|
|
291
|
-
number of qubits for the resulting circuit.
|
|
292
|
-
args (tuple):
|
|
293
|
-
The arguments of the kernel function from which to emit a circuit.
|
|
294
|
-
ignore_returns (bool):
|
|
295
|
-
If `False`, emitting a circuit from a kernel that returns a value will error.
|
|
296
|
-
Set it to `True` in order to ignore the return value(s). Defaults to `False`.
|
|
297
|
-
|
|
298
|
-
"""
|
|
299
|
-
circuit = emit_circuit(
|
|
300
|
-
mt,
|
|
301
|
-
circuit_qubits=circuit_qubits,
|
|
302
|
-
qubits=qubits,
|
|
303
|
-
args=args,
|
|
304
|
-
ignore_returns=ignore_returns,
|
|
305
|
-
)
|
|
306
|
-
return cirq.to_json(circuit, **kwargs)
|