Skip to content

analysis

schedule

StmtDag dataclass

StmtDag(
    id_table: IdTable[Statement] = (
        lambda: idtable.IdTable()
    )(),
    stmts: Dict[str, Statement] = OrderedDict(),
    out_edges: Dict[str, Set[str]] = OrderedDict(),
    inc_edges: Dict[str, Set[str]] = OrderedDict(),
    stmt_index: Dict[Statement, int] = OrderedDict(),
)

Bases: Graph[Statement]

topological_groups

topological_groups()

Split the dag into topological groups where each group contains nodes that have no dependencies on each other, but have dependencies on nodes in one or more previous groups.

Yields:

Type Description

List[str]: A list of node ids in a topological group

Raises:

Type Description
ValueError

If a cyclic dependency is detected

The idea is to yield all nodes with no dependencies, then remove those nodes from the graph repeating until no nodes are left or we reach some upper limit. Worse case is a linear dag, so we can use len(dag.stmts) as the upper limit

If we reach the limit and there are still nodes left, then we have a cyclic dependency.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/analysis/schedule.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def topological_groups(self):
    """Split the dag into topological groups where each group
    contains nodes that have no dependencies on each other, but
    have dependencies on nodes in one or more previous groups.

    Yields:
        List[str]: A list of node ids in a topological group


    Raises:
        ValueError: If a cyclic dependency is detected


    The idea is to yield all nodes with no dependencies, then remove
    those nodes from the graph repeating until no nodes are left
    or we reach some upper limit. Worse case is a linear dag,
    so we can use len(dag.stmts) as the upper limit

    If we reach the limit and there are still nodes left, then we
    have a cyclic dependency.
    """

    inc_edges = {k: set(v) for k, v in self.inc_edges.items()}

    check_next = inc_edges.keys()

    for _ in range(len(self.stmts)):
        if len(inc_edges) == 0:
            break

        group = [node_id for node_id in check_next if len(inc_edges[node_id]) == 0]
        yield group

        check_next = set()
        for n in group:
            inc_edges.pop(n)
            for m in self.out_edges[n]:
                check_next.add(m)
                inc_edges[m].remove(n)

    if inc_edges:
        raise ValueError("Cyclic dependency detected")

cirq

dump_circuit

dump_circuit(
    mt: Method,
    circuit_qubits: Sequence[Qid] | None = None,
    args: tuple = (),
    qubits: Sequence[Qid] | None = None,
    ignore_returns: bool = False,
    **kwargs
)

Converts a squin.kernel method to a cirq.Circuit object and dumps it as JSON.

This just runs emit_circuit and calls the cirq.to_json function to emit a JSON.

Parameters:

Name Type Description Default
mt Method

The kernel method from which to construct the circuit.

required

Other Parameters:

Name Type Description
circuit_qubits Sequence[Qid] | None

A list of qubits to use as the qubits in the circuit. Defaults to None. If this is None, then cirq.LineQubits are inserted for every squin.qubit.new statement in the order they appear inside the kernel. Note: If a list of qubits is provided, make sure that there is a sufficient number of qubits for the resulting circuit.

args tuple

The arguments of the kernel function from which to emit a circuit.

ignore_returns bool

If False, emitting a circuit from a kernel that returns a value will error. Set it to True in order to ignore the return value(s). Defaults to False.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/cirq/__init__.py
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
def dump_circuit(
    mt: ir.Method,
    circuit_qubits: Sequence[cirq.Qid] | None = None,
    args: tuple = (),
    qubits: Sequence[cirq.Qid] | None = None,
    ignore_returns: bool = False,
    **kwargs,
):
    """Converts a squin.kernel method to a cirq.Circuit object and dumps it as JSON.

    This just runs `emit_circuit` and calls the `cirq.to_json` function to emit a JSON.

    Args:
        mt (ir.Method): The kernel method from which to construct the circuit.

    Keyword Args:
        circuit_qubits (Sequence[cirq.Qid] | None):
            A list of qubits to use as the qubits in the circuit. Defaults to None.
            If this is None, then `cirq.LineQubit`s are inserted for every `squin.qubit.new`
            statement in the order they appear inside the kernel.
            **Note**: If a list of qubits is provided, make sure that there is a sufficient
            number of qubits for the resulting circuit.
        args (tuple):
            The arguments of the kernel function from which to emit a circuit.
        ignore_returns (bool):
            If `False`, emitting a circuit from a kernel that returns a value will error.
            Set it to `True` in order to ignore the return value(s). Defaults to `False`.

    """
    circuit = emit_circuit(
        mt,
        circuit_qubits=circuit_qubits,
        qubits=qubits,
        args=args,
        ignore_returns=ignore_returns,
    )
    return cirq.to_json(circuit, **kwargs)

emit_circuit

emit_circuit(
    mt: Method,
    qubits: Sequence[Qid] | None = None,
    circuit_qubits: Sequence[Qid] | None = None,
    args: tuple = (),
    ignore_returns: bool = False,
) -> cirq.Circuit

Converts a squin.kernel method to a cirq.Circuit object.

Parameters:

Name Type Description Default
mt Method

The kernel method from which to construct the circuit.

required

Other Parameters:

Name Type Description
circuit_qubits Sequence[Qid] | None

A list of qubits to use as the qubits in the circuit. Defaults to None. If this is None, then cirq.LineQubits are inserted for every squin.qubit.new statement in the order they appear inside the kernel. Note: If a list of qubits is provided, make sure that there is a sufficient number of qubits for the resulting circuit.

args tuple

The arguments of the kernel function from which to emit a circuit.

ignore_returns bool

If False, emitting a circuit from a kernel that returns a value will error. Set it to True in order to ignore the return value(s). Defaults to False.

Examples:

Here's a very basic example:

from bloqade import squin

@squin.kernel
def main():
    q = squin.qubit.new(2)
    h = squin.op.h()
    squin.qubit.apply(h, q[0])
    cx = squin.op.cx()
    squin.qubit.apply(cx, q)

circuit = squin.cirq.emit_circuit(main)

print(circuit)

You can also compose multiple kernels. Those are emitted as subcircuits within the "main" circuit. Subkernels can accept arguments and return a value.

from bloqade import squin
from kirin.dialects import ilist
from typing import Literal
import cirq

@squin.kernel
def entangle(q: ilist.IList[squin.qubit.Qubit, Literal[2]]):
    h = squin.op.h()
    squin.qubit.apply(h, q[0])
    cx = squin.op.cx()
    squin.qubit.apply(cx, q)
    return cx

@squin.kernel
def main():
    q = squin.qubit.new(2)
    cx = entangle(q)
    q2 = squin.qubit.new(3)
    squin.qubit.apply(cx, [q[1], q2[2]])


# custom list of qubits on grid
qubits = [cirq.GridQubit(i, i+1) for i in range(5)]

circuit = squin.cirq.emit_circuit(main, circuit_qubits=qubits)
print(circuit)

We also passed in a custom list of qubits above. This allows you to provide a custom geometry and manipulate the qubits in other circuits directly written in cirq as well.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/cirq/__init__.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
def emit_circuit(
    mt: ir.Method,
    qubits: Sequence[cirq.Qid] | None = None,
    circuit_qubits: Sequence[cirq.Qid] | None = None,
    args: tuple = (),
    ignore_returns: bool = False,
) -> cirq.Circuit:
    """Converts a squin.kernel method to a cirq.Circuit object.

    Args:
        mt (ir.Method): The kernel method from which to construct the circuit.

    Keyword Args:
        circuit_qubits (Sequence[cirq.Qid] | None):
            A list of qubits to use as the qubits in the circuit. Defaults to None.
            If this is None, then `cirq.LineQubit`s are inserted for every `squin.qubit.new`
            statement in the order they appear inside the kernel.
            **Note**: If a list of qubits is provided, make sure that there is a sufficient
            number of qubits for the resulting circuit.
        args (tuple):
            The arguments of the kernel function from which to emit a circuit.
        ignore_returns (bool):
            If `False`, emitting a circuit from a kernel that returns a value will error.
            Set it to `True` in order to ignore the return value(s). Defaults to `False`.

    ## Examples:

    Here's a very basic example:

    ```python
    from bloqade import squin

    @squin.kernel
    def main():
        q = squin.qubit.new(2)
        h = squin.op.h()
        squin.qubit.apply(h, q[0])
        cx = squin.op.cx()
        squin.qubit.apply(cx, q)

    circuit = squin.cirq.emit_circuit(main)

    print(circuit)
    ```

    You can also compose multiple kernels. Those are emitted as subcircuits within the "main" circuit.
    Subkernels can accept arguments and return a value.

    ```python
    from bloqade import squin
    from kirin.dialects import ilist
    from typing import Literal
    import cirq

    @squin.kernel
    def entangle(q: ilist.IList[squin.qubit.Qubit, Literal[2]]):
        h = squin.op.h()
        squin.qubit.apply(h, q[0])
        cx = squin.op.cx()
        squin.qubit.apply(cx, q)
        return cx

    @squin.kernel
    def main():
        q = squin.qubit.new(2)
        cx = entangle(q)
        q2 = squin.qubit.new(3)
        squin.qubit.apply(cx, [q[1], q2[2]])


    # custom list of qubits on grid
    qubits = [cirq.GridQubit(i, i+1) for i in range(5)]

    circuit = squin.cirq.emit_circuit(main, circuit_qubits=qubits)
    print(circuit)

    ```

    We also passed in a custom list of qubits above. This allows you to provide a custom geometry
    and manipulate the qubits in other circuits directly written in cirq as well.
    """

    if circuit_qubits is None and qubits is not None:
        circuit_qubits = qubits
        warn(
            "The keyword argument `qubits` is deprecated. Use `circuit_qubits` instead."
        )

    if (
        not ignore_returns
        and isinstance(mt.code, func.Function)
        and not mt.code.signature.output.is_subseteq(types.NoneType)
    ):
        raise EmitError(
            "The method you are trying to convert to a circuit has a return value, but returning from a circuit is not supported."
            " Set `ignore_returns = True` in order to simply ignore the return values and emit a circuit."
        )

    if len(args) != len(mt.args):
        raise ValueError(
            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!"
        )

    emitter = EmitCirq(qubits=qubits)

    # Rewrite noise statements
    mt_ = mt.similar(mt.dialects)
    RewriteNoiseStmts(mt_.dialects)(mt_)

    return emitter.run(mt_, args=args)

load_circuit

load_circuit(
    circuit: Circuit,
    kernel_name: str = "main",
    dialects: DialectGroup = kernel,
    register_as_argument: bool = False,
    return_register: bool = False,
    register_argument_name: str = "q",
    globals: dict[str, Any] | None = None,
    file: str | None = None,
    lineno_offset: int = 0,
    col_offset: int = 0,
    compactify: bool = True,
)

Converts a cirq.Circuit object into a squin kernel.

Parameters:

Name Type Description Default
circuit Circuit

The circuit to load.

required

Other Parameters:

Name Type Description
kernel_name str

The name of the kernel to load. Defaults to "main".

dialects DialectGroup | None

The dialects to use. Defaults to squin.kernel.

register_as_argument bool

Determine whether the resulting kernel function should accept a single ilist.IList[Qubit, Any] argument that is a list of qubits used within the function. This allows you to compose kernel functions generated from circuits. Defaults to False.

return_register bool

Determine whether the resulting kernel functionr returns a single value of type ilist.IList[Qubit, Any] that is the list of qubits used in the kernel function. Useful when you want to compose multiple kernel functions generated from circuits. Defaults to False.

register_argument_name str

The name of the argument that represents the qubit register. Only used when register_as_argument=True. Defaults to "q".

globals dict[str, Any] | None

The global variables to use. Defaults to None.

file str | None

The file name for error reporting. Defaults to None.

lineno_offset int

The line number offset for error reporting. Defaults to 0.

col_offset int

The column number offset for error reporting. Defaults to 0.

compactify bool

Whether to compactify the output. Defaults to True.

Usage Examples:

# from cirq's "hello qubit" example
import cirq
from bloqade import squin

# Pick a qubit.
qubit = cirq.GridQubit(0, 0)

# Create a circuit.
circuit = cirq.Circuit(
    cirq.X(qubit)**0.5,  # Square root of NOT.
    cirq.measure(qubit, key='m')  # Measurement.
)

# load the circuit as squin
main = squin.load_circuit(circuit)

# print the resulting IR
main.print()

You can also compose kernel functions generated from circuits by passing in and / or returning the respective quantum registers:

q = cirq.LineQubit.range(2)
circuit = cirq.Circuit(cirq.H(q[0]), cirq.CX(*q))

get_entangled_qubits = squin.cirq.load_circuit(
    circuit, return_register=True, kernel_name="get_entangled_qubits"
)
get_entangled_qubits.print()

entangle_qubits = squin.cirq.load_circuit(
    circuit, register_as_argument=True, kernel_name="entangle_qubits"
)

@squin.kernel
def main():
    qreg = get_entangled_qubits()
    qreg2 = squin.qubit.new(1)
    entangle_qubits([qreg[1], qreg2[0]])
    return squin.qubit.measure(qreg2)
Source code in .venv/lib/python3.12/site-packages/bloqade/squin/cirq/__init__.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def load_circuit(
    circuit: cirq.Circuit,
    kernel_name: str = "main",
    dialects: ir.DialectGroup = kernel,
    register_as_argument: bool = False,
    return_register: bool = False,
    register_argument_name: str = "q",
    globals: dict[str, Any] | None = None,
    file: str | None = None,
    lineno_offset: int = 0,
    col_offset: int = 0,
    compactify: bool = True,
):
    """Converts a cirq.Circuit object into a squin kernel.

    Args:
        circuit (cirq.Circuit): The circuit to load.

    Keyword Args:
        kernel_name (str): The name of the kernel to load. Defaults to "main".
        dialects (ir.DialectGroup | None): The dialects to use. Defaults to `squin.kernel`.
        register_as_argument (bool): Determine whether the resulting kernel function should accept
            a single `ilist.IList[Qubit, Any]` argument that is a list of qubits used within the
            function. This allows you to compose kernel functions generated from circuits.
            Defaults to `False`.
        return_register (bool): Determine whether the resulting kernel functionr returns a
            single value of type `ilist.IList[Qubit, Any]` that is the list of qubits used
            in the kernel function. Useful when you want to compose multiple kernel functions
            generated from circuits. Defaults to `False`.
        register_argument_name (str): The name of the argument that represents the qubit register.
            Only used when `register_as_argument=True`. Defaults to "q".
        globals (dict[str, Any] | None): The global variables to use. Defaults to None.
        file (str | None): The file name for error reporting. Defaults to None.
        lineno_offset (int): The line number offset for error reporting. Defaults to 0.
        col_offset (int): The column number offset for error reporting. Defaults to 0.
        compactify (bool): Whether to compactify the output. Defaults to True.

    ## Usage Examples:

    ```python
    # from cirq's "hello qubit" example
    import cirq
    from bloqade import squin

    # Pick a qubit.
    qubit = cirq.GridQubit(0, 0)

    # Create a circuit.
    circuit = cirq.Circuit(
        cirq.X(qubit)**0.5,  # Square root of NOT.
        cirq.measure(qubit, key='m')  # Measurement.
    )

    # load the circuit as squin
    main = squin.load_circuit(circuit)

    # print the resulting IR
    main.print()
    ```

    You can also compose kernel functions generated from circuits by passing in
    and / or returning the respective quantum registers:

    ```python
    q = cirq.LineQubit.range(2)
    circuit = cirq.Circuit(cirq.H(q[0]), cirq.CX(*q))

    get_entangled_qubits = squin.cirq.load_circuit(
        circuit, return_register=True, kernel_name="get_entangled_qubits"
    )
    get_entangled_qubits.print()

    entangle_qubits = squin.cirq.load_circuit(
        circuit, register_as_argument=True, kernel_name="entangle_qubits"
    )

    @squin.kernel
    def main():
        qreg = get_entangled_qubits()
        qreg2 = squin.qubit.new(1)
        entangle_qubits([qreg[1], qreg2[0]])
        return squin.qubit.measure(qreg2)
    ```
    """

    target = Squin(dialects=dialects, circuit=circuit)
    body = target.run(
        circuit,
        source=str(circuit),  # TODO: proper source string
        file=file,
        globals=globals,
        lineno_offset=lineno_offset,
        col_offset=col_offset,
        compactify=compactify,
        register_as_argument=register_as_argument,
        register_argument_name=register_argument_name,
    )

    if return_register:
        return_value = target.qreg
    else:
        return_value = func.ConstantNone()
        body.blocks[0].stmts.append(return_value)

    return_node = func.Return(value_or_stmt=return_value)
    body.blocks[0].stmts.append(return_node)

    self_arg_name = kernel_name + "_self"
    arg_names = [self_arg_name]
    if register_as_argument:
        args = (target.qreg.type,)
        arg_names.append(register_argument_name)
    else:
        args = ()

    # NOTE: add _self as argument; need to know signature before so do it after lowering
    signature = func.Signature(args, return_node.value.type)
    body.blocks[0].args.insert_from(
        0,
        types.Generic(ir.Method, types.Tuple.where(signature.inputs), signature.output),
        self_arg_name,
    )

    code = func.Function(
        sym_name=kernel_name,
        signature=signature,
        body=body,
    )

    return ir.Method(
        mod=None,
        py_func=None,
        sym_name=kernel_name,
        arg_names=arg_names,
        dialects=dialects,
        code=code,
    )

lowering

Squin dataclass

Squin(circuit: Circuit)

Bases: LoweringABC[CirqNode]

Lower a cirq.Circuit object to a squin kernel

gate

ch

ch(control: Qubit, target: Qubit) -> None

Controlled Hadamard gate applied to control and target

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
168
169
170
171
172
@kernel
def ch(control: Qubit, target: Qubit) -> None:
    """Controlled Hadamard gate applied to control and target"""
    op = _op.ch()
    _qubit.apply(op, control, target)

cx

cx(control: Qubit, target: Qubit) -> None

Controlled x gate applied to control and target

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
147
148
149
150
151
@kernel
def cx(control: Qubit, target: Qubit) -> None:
    """Controlled x gate applied to control and target"""
    op = _op.cx()
    _qubit.apply(op, control, target)

cy

cy(control: Qubit, target: Qubit) -> None

Controlled y gate applied to control and target

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
154
155
156
157
158
@kernel
def cy(control: Qubit, target: Qubit) -> None:
    """Controlled y gate applied to control and target"""
    op = _op.cy()
    _qubit.apply(op, control, target)

cz

cz(control: Qubit, target: Qubit) -> None

Controlled z gate applied to control and target

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
161
162
163
164
165
@kernel
def cz(control: Qubit, target: Qubit) -> None:
    """Controlled z gate applied to control and target"""
    op = _op.cz()
    _qubit.apply(op, control, target)

h

h(qubit: Qubit) -> None

Hadamard gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
70
71
72
73
74
@kernel
def h(qubit: Qubit) -> None:
    """Hadamard gate applied to qubit."""
    op = _op.h()
    _qubit.apply(op, qubit)

p0

p0(qubit: Qubit) -> None

Projector on 0 applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
105
106
107
108
109
@kernel
def p0(qubit: Qubit) -> None:
    """Projector on 0 applied to qubit."""
    op = _op.p0()
    _qubit.apply(op, qubit)

p1

p1(qubit: Qubit) -> None

Projector on 1 applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
112
113
114
115
116
@kernel
def p1(qubit: Qubit) -> None:
    """Projector on 1 applied to qubit."""
    op = _op.p1()
    _qubit.apply(op, qubit)

reset

reset(qubit: Qubit) -> None

Reset qubit to 0.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
133
134
135
136
137
@kernel
def reset(qubit: Qubit) -> None:
    """Reset qubit to 0."""
    op = _op.reset()
    _qubit.apply(op, qubit)

reset_to_one

reset_to_one(qubit: Qubit) -> None

Reset qubit to 1.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
140
141
142
143
144
@kernel
def reset_to_one(qubit: Qubit) -> None:
    """Reset qubit to 1."""
    op = _op.reset_to_one()
    _qubit.apply(op, qubit)

rx

rx(theta: float, qubit: Qubit) -> None

Rotation X gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
182
183
184
185
186
@kernel
def rx(theta: float, qubit: Qubit) -> None:
    """Rotation X gate applied to qubit."""
    op = _op.rot(_op.x(), theta)
    _qubit.apply(op, qubit)

ry

ry(theta: float, qubit: Qubit) -> None

Rotation Y gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
189
190
191
192
193
@kernel
def ry(theta: float, qubit: Qubit) -> None:
    """Rotation Y gate applied to qubit."""
    op = _op.rot(_op.y(), theta)
    _qubit.apply(op, qubit)

rz

rz(theta: float, qubit: Qubit) -> None

Rotation Z gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
196
197
198
199
200
@kernel
def rz(theta: float, qubit: Qubit) -> None:
    """Rotation Z gate applied to qubit."""
    op = _op.rot(_op.z(), theta)
    _qubit.apply(op, qubit)

s

s(qubit: Qubit) -> None

s gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
77
78
79
80
81
@kernel
def s(qubit: Qubit) -> None:
    """s gate applied to qubit."""
    op = _op.s()
    _qubit.apply(op, qubit)

s_adj

s_adj(qubit: Qubit) -> None

Adjoint s gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
84
85
86
87
88
@kernel
def s_adj(qubit: Qubit) -> None:
    """Adjoint s gate applied to qubit."""
    op = _op.s()
    _qubit.apply(_op.adjoint(op), qubit)

spin_n

spin_n(qubit: Qubit) -> None

Spin lowering gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
119
120
121
122
123
@kernel
def spin_n(qubit: Qubit) -> None:
    """Spin lowering gate applied to qubit."""
    op = _op.spin_n()
    _qubit.apply(op, qubit)

spin_p

spin_p(qubit: Qubit) -> None

Spin raising gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
126
127
128
129
130
@kernel
def spin_p(qubit: Qubit) -> None:
    """Spin raising gate applied to qubit."""
    op = _op.spin_p()
    _qubit.apply(op, qubit)

sqrt_x

sqrt_x(qubit: Qubit) -> None

Square root x gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
28
29
30
31
32
@kernel
def sqrt_x(qubit: Qubit) -> None:
    """Square root x gate applied to qubit."""
    op = _op.sqrt_x()
    _qubit.apply(op, qubit)

sqrt_x_adj

sqrt_x_adj(qubit: Qubit) -> None

Adjoint sqrt_x gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
35
36
37
38
39
@kernel
def sqrt_x_adj(qubit: Qubit) -> None:
    """Adjoint sqrt_x gate applied to qubit."""
    op = _op.sqrt_x()
    _qubit.apply(_op.adjoint(op), qubit)

sqrt_y

sqrt_y(qubit: Qubit) -> None

Square root y gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
42
43
44
45
46
@kernel
def sqrt_y(qubit: Qubit) -> None:
    """Square root y gate applied to qubit."""
    op = _op.sqrt_y()
    _qubit.apply(op, qubit)

sqrt_y_adj

sqrt_y_adj(qubit: Qubit) -> None

Adjoint sqrt_y gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
49
50
51
52
53
@kernel
def sqrt_y_adj(qubit: Qubit) -> None:
    """Adjoint sqrt_y gate applied to qubit."""
    op = _op.sqrt_y()
    _qubit.apply(_op.adjoint(op), qubit)

sqrt_z

sqrt_z(qubit: Qubit) -> None

Square root z gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
56
57
58
59
60
@kernel
def sqrt_z(qubit: Qubit) -> None:
    """Square root z gate applied to qubit."""
    op = _op.s()
    _qubit.apply(op, qubit)

sqrt_z_adj

sqrt_z_adj(qubit: Qubit) -> None

Adjoint square root z gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
63
64
65
66
67
@kernel
def sqrt_z_adj(qubit: Qubit) -> None:
    """Adjoint square root z gate applied to qubit."""
    op = _op.s()
    _qubit.apply(_op.adjoint(op), qubit)

t

t(qubit: Qubit) -> None

t gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
91
92
93
94
95
@kernel
def t(qubit: Qubit) -> None:
    """t gate applied to qubit."""
    op = _op.t()
    _qubit.apply(op, qubit)

t_adj

t_adj(qubit: Qubit) -> None

Adjoint t gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
 98
 99
100
101
102
@kernel
def t_adj(qubit: Qubit) -> None:
    """Adjoint t gate applied to qubit."""
    op = _op.t()
    _qubit.apply(_op.adjoint(op), qubit)

u

u(
    theta: float, phi: float, lam: float, qubit: Qubit
) -> None

3D rotation gate applied to control and target

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
175
176
177
178
179
@kernel
def u(theta: float, phi: float, lam: float, qubit: Qubit) -> None:
    """3D rotation gate applied to control and target"""
    op = _op.u(theta, phi, lam)
    _qubit.apply(op, qubit)

x

x(qubit: Qubit) -> None

x gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
 7
 8
 9
10
11
@kernel
def x(qubit: Qubit) -> None:
    """x gate applied to qubit."""
    op = _op.x()
    _qubit.apply(op, qubit)

y

y(qubit: Qubit) -> None

y gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
14
15
16
17
18
@kernel
def y(qubit: Qubit) -> None:
    """y gate applied to qubit."""
    op = _op.y()
    _qubit.apply(op, qubit)

z

z(qubit: Qubit) -> None

z gate applied to qubit.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/gate.py
21
22
23
24
25
@kernel
def z(qubit: Qubit) -> None:
    """z gate applied to qubit."""
    op = _op.z()
    _qubit.apply(op, qubit)

lowering

ApplyAnyCallLowering dataclass

ApplyAnyCallLowering()

Bases: FromPythonCall['qubit.ApplyAny']

Custom lowering for ApplyAny that collects vararg qubits into a single tuple argument

BroadcastCallLowering dataclass

BroadcastCallLowering()

Bases: FromPythonCall['qubit.Broadcast']

Custom lowering for broadcast vararg call.

NOTE: we can re-use this to lower Apply too once we remove the deprecated syntax

noise

rewrite

stmts

Depolarize

Bases: NoiseChannel

Apply depolarize error to single qubit

Depolarize2

Bases: NoiseChannel

Apply correlated depolarize error to two qubit

op

rewrite

Rewrite py.binop.mult to Mult stmt

stdlib

ch

ch() -> types.Op

Control H gate.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/op/stdlib.py
53
54
55
56
@op
def ch() -> types.Op:
    """Control H gate."""
    return control(h(), n_controls=1)

cphase

cphase(theta: float) -> types.Op

Control Phase gate.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/op/stdlib.py
59
60
61
62
@op
def cphase(theta: float) -> types.Op:
    """Control Phase gate."""
    return control(phase(theta), n_controls=1)

cx

cx() -> types.Op

Controlled X gate.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/op/stdlib.py
35
36
37
38
@op
def cx() -> types.Op:
    """Controlled X gate."""
    return control(x(), n_controls=1)

cy

cy() -> types.Op

Controlled Y gate.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/op/stdlib.py
41
42
43
44
@op
def cy() -> types.Op:
    """Controlled Y gate."""
    return control(y(), n_controls=1)

cz

cz() -> types.Op

Control Z gate.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/op/stdlib.py
47
48
49
50
@op
def cz() -> types.Op:
    """Control Z gate."""
    return control(z(), n_controls=1)

rx

rx(theta: float) -> types.Op

Rotation X gate.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/op/stdlib.py
17
18
19
20
@op
def rx(theta: float) -> types.Op:
    """Rotation X gate."""
    return rot(x(), theta)

ry

ry(theta: float) -> types.Op

Rotation Y gate.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/op/stdlib.py
23
24
25
26
@op
def ry(theta: float) -> types.Op:
    """Rotation Y gate."""
    return rot(y(), theta)

rz

rz(theta: float) -> types.Op

Rotation Z gate.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/op/stdlib.py
29
30
31
32
@op
def rz(theta: float) -> types.Op:
    """Rotation Z gate."""
    return rot(z(), theta)

stmts

P0

Bases: ConstantOp

The \(P_0\) projection operator.

\[ P0 = \begin{bmatrix} 1 & 0 \\ 0 & 0 \end{bmatrix} \]

P1

Bases: ConstantOp

The \(P_1\) projection operator.

\[ P1 = \begin{bmatrix} 0 & 0 \\ 0 & 1 \end{bmatrix} \]

PhaseOp

Bases: PrimitiveOp

A phase operator.

\[ \text{PhaseOp}(\theta) = e^{i \theta} I \]

Reset

Bases: PrimitiveOp

Reset operator for qubits and wires.

ResetToOne

Bases: PrimitiveOp

Reset qubits to the one state. Mainly needed to accommodate cirq's GeneralizedAmplitudeDampingChannel

ShiftOp

Bases: PrimitiveOp

A phase shift operator.

\[ \text{Shift}(\theta) = \begin{bmatrix} 1 & 0 \\ 0 & e^{i \theta} \end{bmatrix} \]

Sn

Bases: ConstantOp

\(S_{-}\) operator.

\[ Sn = \frac{1}{2} (S_x - i S_y) = \frac{1}{2} \begin{bmatrix} 0 & 0 \\ 1 & 0 \end{bmatrix} \]

Sp

Bases: ConstantOp

\(S_{+}\) operator.

\[ Sp = \frac{1}{2} (S_x + i S_y) = \frac{1}{2}\begin{bmatrix} 0 & 1 \\ 0 & 0 \end{bmatrix} \]

U3

Bases: PrimitiveOp

The rotation operator U3(theta, phi, lam). Note that we use the convention from the QASM2 specification, namely

\[ U_3(\theta, \phi, \lambda) = R_z(\phi) R_y(\theta) R_z(\lambda) \]

traits

HasSites dataclass

HasSites()

Bases: StmtTrait

An operator with a sites attribute.

parallel

ch

ch(
    controls: IList[Qubit, N], targets: IList[Qubit, N]
) -> None

Controlled Hadamard gate applied to controls and targets in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
133
134
135
136
137
@kernel
def ch(controls: ilist.IList[Qubit, N], targets: ilist.IList[Qubit, N]) -> None:
    """Controlled Hadamard gate applied to controls and targets in parallel."""
    op = _op.ch()
    _qubit.broadcast(op, controls, targets)

cx

cx(
    controls: IList[Qubit, N], targets: IList[Qubit, N]
) -> None

Controlled x gate applied to controls and targets in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
112
113
114
115
116
@kernel
def cx(controls: ilist.IList[Qubit, N], targets: ilist.IList[Qubit, N]) -> None:
    """Controlled x gate applied to controls and targets in parallel."""
    op = _op.cx()
    _qubit.broadcast(op, controls, targets)

cy

cy(
    controls: IList[Qubit, N], targets: IList[Qubit, N]
) -> None

Controlled y gate applied to controls and targets in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
119
120
121
122
123
@kernel
def cy(controls: ilist.IList[Qubit, N], targets: ilist.IList[Qubit, N]) -> None:
    """Controlled y gate applied to controls and targets in parallel."""
    op = _op.cy()
    _qubit.broadcast(op, controls, targets)

cz

cz(
    controls: IList[Qubit, N], targets: IList[Qubit, N]
) -> None

Controlled z gate applied to controls and targets in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
126
127
128
129
130
@kernel
def cz(controls: ilist.IList[Qubit, N], targets: ilist.IList[Qubit, N]) -> None:
    """Controlled z gate applied to controls and targets in parallel."""
    op = _op.cz()
    _qubit.broadcast(op, controls, targets)

h

h(qubits: IList[Qubit, Any]) -> None

Hadamard gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
53
54
55
56
57
@kernel
def h(qubits: ilist.IList[Qubit, Any]) -> None:
    """Hadamard gate applied to qubits in parallel."""
    op = _op.h()
    _qubit.broadcast(op, qubits)

p0

p0(qubits: IList[Qubit, Any]) -> None

Projector on 0 applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
74
75
76
77
78
@kernel
def p0(qubits: ilist.IList[Qubit, Any]) -> None:
    """Projector on 0 applied to qubits in parallel."""
    op = _op.p0()
    _qubit.broadcast(op, qubits)

p1

p1(qubits: IList[Qubit, Any]) -> None

Projector on 1 applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
81
82
83
84
85
@kernel
def p1(qubits: ilist.IList[Qubit, Any]) -> None:
    """Projector on 1 applied to qubits in parallel."""
    op = _op.p1()
    _qubit.broadcast(op, qubits)

reset

reset(qubits: IList[Qubit, Any]) -> None

Reset qubit to 0.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
102
103
104
105
106
@kernel
def reset(qubits: ilist.IList[Qubit, Any]) -> None:
    """Reset qubit to 0."""
    op = _op.reset()
    _qubit.broadcast(op, qubits)

rx

rx(theta: float, qubits: IList[Qubit, Any]) -> None

Rotation X gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
147
148
149
150
151
@kernel
def rx(theta: float, qubits: ilist.IList[Qubit, Any]) -> None:
    """Rotation X gate applied to qubits in parallel."""
    op = _op.rot(_op.x(), theta)
    _qubit.broadcast(op, qubits)

ry

ry(theta: float, qubits: IList[Qubit, Any]) -> None

Rotation Y gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
154
155
156
157
158
@kernel
def ry(theta: float, qubits: ilist.IList[Qubit, Any]) -> None:
    """Rotation Y gate applied to qubits in parallel."""
    op = _op.rot(_op.y(), theta)
    _qubit.broadcast(op, qubits)

rz

rz(theta: float, qubits: IList[Qubit, Any]) -> None

Rotation Z gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
161
162
163
164
165
@kernel
def rz(theta: float, qubits: ilist.IList[Qubit, Any]) -> None:
    """Rotation Z gate applied to qubits in parallel."""
    op = _op.rot(_op.z(), theta)
    _qubit.broadcast(op, qubits)

s

s(qubits: IList[Qubit, Any]) -> None

s gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
60
61
62
63
64
@kernel
def s(qubits: ilist.IList[Qubit, Any]) -> None:
    """s gate applied to qubits in parallel."""
    op = _op.s()
    _qubit.broadcast(op, qubits)

s_adj

s_adj(qubits: IList[Qubit, Any]) -> None

Adjoint s gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
189
190
191
192
193
@kernel
def s_adj(qubits: ilist.IList[Qubit, Any]) -> None:
    """Adjoint s gate applied to qubits in parallel."""
    op = _op.s()
    _qubit.broadcast(_op.adjoint(op), qubits)

spin_n

spin_n(qubits: IList[Qubit, Any]) -> None

Spin lowering gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
88
89
90
91
92
@kernel
def spin_n(qubits: ilist.IList[Qubit, Any]) -> None:
    """Spin lowering gate applied to qubits in parallel."""
    op = _op.spin_n()
    _qubit.broadcast(op, qubits)

spin_p

spin_p(qubits: IList[Qubit, Any]) -> None

Spin raising gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
95
96
97
98
99
@kernel
def spin_p(qubits: ilist.IList[Qubit, Any]) -> None:
    """Spin raising gate applied to qubits in parallel."""
    op = _op.spin_p()
    _qubit.broadcast(op, qubits)

sqrt_x

sqrt_x(qubits: IList[Qubit, Any]) -> None

Square root x gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
32
33
34
35
36
@kernel
def sqrt_x(qubits: ilist.IList[Qubit, Any]) -> None:
    """Square root x gate applied to qubits in parallel."""
    op = _op.sqrt_x()
    _qubit.broadcast(op, qubits)

sqrt_x_adj

sqrt_x_adj(qubits: IList[Qubit, Any]) -> None

Adjoint sqrt_x gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
168
169
170
171
172
@kernel
def sqrt_x_adj(qubits: ilist.IList[Qubit, Any]) -> None:
    """Adjoint sqrt_x gate applied to qubits in parallel."""
    op = _op.sqrt_x()
    _qubit.broadcast(_op.adjoint(op), qubits)

sqrt_y

sqrt_y(qubits: IList[Qubit, Any]) -> None

Square root y gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
39
40
41
42
43
@kernel
def sqrt_y(qubits: ilist.IList[Qubit, Any]) -> None:
    """Square root y gate applied to qubits in parallel."""
    op = _op.sqrt_y()
    _qubit.broadcast(op, qubits)

sqrt_y_adj

sqrt_y_adj(qubits: IList[Qubit, Any]) -> None

Adjoint sqrt_y gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
175
176
177
178
179
@kernel
def sqrt_y_adj(qubits: ilist.IList[Qubit, Any]) -> None:
    """Adjoint sqrt_y gate applied to qubits in parallel."""
    op = _op.sqrt_y()
    _qubit.broadcast(_op.adjoint(op), qubits)

sqrt_z

sqrt_z(qubits: IList[Qubit, Any]) -> None

Square root gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
46
47
48
49
50
@kernel
def sqrt_z(qubits: ilist.IList[Qubit, Any]) -> None:
    """Square root gate applied to qubits in parallel."""
    op = _op.s()
    _qubit.broadcast(op, qubits)

sqrt_z_adj

sqrt_z_adj(qubits: IList[Qubit, Any]) -> None

Adjoint square root z gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
182
183
184
185
186
@kernel
def sqrt_z_adj(qubits: ilist.IList[Qubit, Any]) -> None:
    """Adjoint square root z gate applied to qubits in parallel."""
    op = _op.s()
    _qubit.broadcast(_op.adjoint(op), qubits)

t

t(qubits: IList[Qubit, Any]) -> None

t gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
67
68
69
70
71
@kernel
def t(qubits: ilist.IList[Qubit, Any]) -> None:
    """t gate applied to qubits in parallel."""
    op = _op.t()
    _qubit.broadcast(op, qubits)

t_adj

t_adj(qubits: IList[Qubit, Any]) -> None

Adjoint t gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
196
197
198
199
200
@kernel
def t_adj(qubits: ilist.IList[Qubit, Any]) -> None:
    """Adjoint t gate applied to qubits in parallel."""
    op = _op.t()
    _qubit.broadcast(_op.adjoint(op), qubits)

u

u(
    theta: float,
    phi: float,
    lam: float,
    qubits: IList[Qubit, Any],
) -> None

3D rotation gate applied to controls and targets in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
140
141
142
143
144
@kernel
def u(theta: float, phi: float, lam: float, qubits: ilist.IList[Qubit, Any]) -> None:
    """3D rotation gate applied to controls and targets in parallel."""
    op = _op.u(theta, phi, lam)
    _qubit.broadcast(op, qubits)

x

x(qubits: IList[Qubit, Any]) -> None

x gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
11
12
13
14
15
@kernel
def x(qubits: ilist.IList[Qubit, Any]) -> None:
    """x gate applied to qubits in parallel."""
    op = _op.x()
    _qubit.broadcast(op, qubits)

y

y(qubits: IList[Qubit, Any]) -> None

y gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
18
19
20
21
22
@kernel
def y(qubits: ilist.IList[Qubit, Any]) -> None:
    """y gate applied to qubits in parallel."""
    op = _op.y()
    _qubit.broadcast(op, qubits)

z

z(qubits: IList[Qubit, Any]) -> None

z gate applied to qubits in parallel.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/parallel.py
25
26
27
28
29
@kernel
def z(qubits: ilist.IList[Qubit, Any]) -> None:
    """z gate applied to qubits in parallel."""
    op = _op.z()
    _qubit.broadcast(op, qubits)

qubit

qubit dialect for squin language.

This dialect defines the operations that can be performed on qubits.

Depends on: - bloqade.squin.op: provides the OpType type and semantics for operators applied to qubits. - kirin.dialects.ilist: provides the ilist.IListType type for lists of qubits.

apply

apply(operator: Op, *qubits: Qubit) -> None

Apply an operator to qubits. The number of qubit arguments must match the size of the operator.

Note, that when considering atom loss, lost qubits will be skipped.

Parameters:

Name Type Description Default
operator Op

The operator to apply.

required
*qubits Qubit

The qubits to apply the operator to. The number of qubits must match the size of the operator.

()

Returns:

Type Description
None

None

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/qubit.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
@wraps(ApplyAny)
def apply(operator: Op, *qubits: Qubit) -> None:
    """Apply an operator to qubits. The number of qubit arguments must match the
    size of the operator.

    Note, that when considering atom loss, lost qubits will be skipped.

    Args:
        operator: The operator to apply.
        *qubits: The qubits to apply the operator to. The number of qubits must
            match the size of the operator.

    Returns:
        None
    """
    ...

broadcast

broadcast(
    operator: Op,
    *qubits: IList[Qubit, OpSize] | list[Qubit]
) -> None

Broadcast and apply an operator to lists of qubits. The number of qubit lists must match the size of the operator and the lists must be of same length. The operator is then applied to the list elements similar to what python's map function does.

Usage examples

from bloqade import squin

@squin.kernel
def ghz():
    controls = squin.qubit.new(4)
    targets = squin.qubit.new(4)

    h = squin.op.h()
    squin.qubit.broadcast(h, controls)

    cx = squin.op.cx()
    squin.qubit.broadcast(cx, controls, targets)

Parameters:

Name Type Description Default
operator Op

The operator to broadcast and apply.

required
qubits IList[Qubit, OpSize] | list[Qubit]

The list of qubits to broadcast and apply the operator to. The size of the list must be inferable and match the number of qubits expected by the operator.

()

Returns:

Type Description
None

None

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/qubit.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
@wraps(Broadcast)
def broadcast(operator: Op, *qubits: ilist.IList[Qubit, OpSize] | list[Qubit]) -> None:
    """Broadcast and apply an operator to lists of qubits. The number of qubit lists must
    match the size of the operator and the lists must be of same length. The operator is
    then applied to the list elements similar to what python's map function does.

    ## Usage examples

    ```python
    from bloqade import squin

    @squin.kernel
    def ghz():
        controls = squin.qubit.new(4)
        targets = squin.qubit.new(4)

        h = squin.op.h()
        squin.qubit.broadcast(h, controls)

        cx = squin.op.cx()
        squin.qubit.broadcast(cx, controls, targets)
    ```

    Args:
        operator: The operator to broadcast and apply.
        qubits: The list of qubits to broadcast and apply the operator to. The size of the list
            must be inferable and match the number of qubits expected by the operator.

    Returns:
        None
    """
    ...

measure

measure(input: Qubit) -> MeasurementResult
measure(
    input: IList[Qubit, Any] | list[Qubit],
) -> ilist.IList[MeasurementResult, Any]
measure(input: Any) -> Any

Measure a qubit or qubits in the list.

Parameters:

Name Type Description Default
input Any

A qubit or a list of qubits to measure.

required

Returns:

Type Description
Any

bool | list[bool]: The result of the measurement. If a single qubit is measured, a single boolean is returned. If a list of qubits is measured, a list of booleans is returned.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/qubit.py
122
123
124
125
126
127
128
129
130
131
132
133
134
@wraps(MeasureAny)
def measure(input: Any) -> Any:
    """Measure a qubit or qubits in the list.

    Args:
        input: A qubit or a list of qubits to measure.

    Returns:
        bool | list[bool]: The result of the measurement. If a single qubit is measured,
            a single boolean is returned. If a list of qubits is measured, a list of booleans
            is returned.
    """
    ...

new

new(n_qubits: int) -> ilist.IList[Qubit, Any]

Create a new list of qubits.

Parameters:

Name Type Description Default
n_qubits(int)

The number of qubits to create.

required

Returns:

Type Description
IList[Qubit, Any]

(ilist.IList[Qubit, n_qubits]) A list of qubits.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/qubit.py
83
84
85
86
87
88
89
90
91
92
93
@wraps(New)
def new(n_qubits: int) -> ilist.IList[Qubit, Any]:
    """Create a new list of qubits.

    Args:
        n_qubits(int): The number of qubits to create.

    Returns:
        (ilist.IList[Qubit, n_qubits]) A list of qubits.
    """
    ...

rewrite

U3_to_clifford

SquinU3ToClifford

Bases: RewriteRule

Rewrite squin U3 statements to clifford when possible.

decompose_U3_gates

decompose_U3_gates(
    node: U3,
) -> Tuple[List[ir.Statement], ...]

Rewrite U3 statements to clifford gates if possible.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/U3_to_clifford.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
def decompose_U3_gates(self, node: op.stmts.U3) -> Tuple[List[ir.Statement], ...]:
    """
    Rewrite U3 statements to clifford gates if possible.
    """
    theta = self.get_constant(node.theta)
    phi = self.get_constant(node.phi)
    lam = self.get_constant(node.lam)

    if theta is None or phi is None or lam is None:
        return ()

    # For U3(2*pi*n, phi, lam) = U3(0, 0, lam + phi) which is a Z rotation.
    if np.isclose(np.mod(theta, math.tau), 0):
        lam = lam + phi
        phi = 0.0
    elif np.isclose(np.mod(theta + np.pi, math.tau), 0):
        lam = lam - phi
        phi = 0.0

    theta_half_pi: int | None = self.resolve_angle(theta)
    phi_half_pi: int | None = self.resolve_angle(phi)
    lam_half_pi: int | None = self.resolve_angle(lam)

    if theta_half_pi is None or phi_half_pi is None or lam_half_pi is None:
        return ()

    angles_key = (theta_half_pi, phi_half_pi, lam_half_pi)
    if angles_key not in U3_HALF_PI_ANGLE_TO_GATES:
        angles_key = equivalent_u3_para(*angles_key)
        if angles_key not in U3_HALF_PI_ANGLE_TO_GATES:
            return ()

    gates_stmts = U3_HALF_PI_ANGLE_TO_GATES.get(angles_key)

    # no consistent gates, then:
    assert (
        gates_stmts is not None
    ), "internal error, U3 gates not found for angles: {}".format(angles_key)

    return gates_stmts()

resolve_angle

resolve_angle(angle: float) -> int | None

Normalize the angle to be in the range [0, 2π).

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/U3_to_clifford.py
76
77
78
79
80
81
82
83
84
85
86
87
88
def resolve_angle(self, angle: float) -> int | None:
    """
    Normalize the angle to be in the range [0, 2π).
    """
    # convert to 0.0~1.0, in unit of pi/2
    angle_half_pi = angle / math.pi * 2.0

    mod = angle_half_pi % 1.0
    if not (np.isclose(mod, 0.0) or np.isclose(mod, 1.0)):
        return None

    else:
        return round((angle / math.tau) % 1 * 4) % 4

rewrite_ApplyOrBroadcast_onU3

rewrite_ApplyOrBroadcast_onU3(
    node: Apply | Broadcast,
) -> RewriteResult

Rewrite Apply and Broadcast nodes to their clifford equivalent statements.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/U3_to_clifford.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def rewrite_ApplyOrBroadcast_onU3(
    self, node: qubit.Apply | qubit.Broadcast
) -> RewriteResult:
    """
    Rewrite Apply and Broadcast nodes to their clifford equivalent statements.
    """
    if not isinstance(node.operator.owner, op.stmts.U3):
        return RewriteResult()

    gates = self.decompose_U3_gates(node.operator.owner)

    if len(gates) == 0:
        return RewriteResult()

    for stmt_list in gates:
        for gate_stmt in stmt_list[:-1]:
            gate_stmt.insert_before(node)

        oper = stmt_list[-1]
        oper.insert_before(node)
        new_node = node.__class__(operator=oper.result, qubits=node.qubits)
        new_node.insert_before(node)

    node.delete()

    # rewrite U3 to clifford gates
    return RewriteResult(has_done_something=True)

equivalent_u3_para

equivalent_u3_para(
    theta_half_pi: int, phi_half_pi: int, lam_half_pi: int
) -> tuple[int, int, int]
  1. Assume all three angles are in the range [0, 4].
  2. U3(theta, phi, lam) = -U3(2pi-theta, phi+pi, lam+pi).
Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/U3_to_clifford.py
48
49
50
51
52
53
54
55
def equivalent_u3_para(
    theta_half_pi: int, phi_half_pi: int, lam_half_pi: int
) -> tuple[int, int, int]:
    """
    1. Assume all three angles are in the range [0, 4].
    2. U3(theta, phi, lam) = -U3(2pi-theta, phi+pi, lam+pi).
    """
    return ((4 - theta_half_pi) % 4, (phi_half_pi + 2) % 4, (lam_half_pi + 2) % 4)

desugar

ApplyDesugarRule

Bases: RewriteRule

Desugar apply operators in the kernel.

NOTE: this pass can be removed once we decide to disallow the syntax apply(op: Op, qubits: list)

MeasureDesugarRule

Bases: RewriteRule

Desugar measure operations in the circuit.

wire

A NVIDIA QUAKE-like wire dialect.

This dialect is expected to be used in combination with the operator dialect as an intermediate representation for analysis and optimization of quantum circuits. Thus we do not define wrapping functions for the statements in this dialect.