Skip to content

Index

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,
    )