SQUIN
The Structural Quantum Instructions (SQUIN) dialect constitutes the central domain-specific language used in bloqade-circuit.
It allows you define your program in terms of gates applied to qubits, adding powerful control flow, such as for loops.
Squin overview
Section titled “Squin overview”The SQUIN DSL consists of three sub-groups of dialects:
squin.qubit, which can be used for manipulating qubits via gate applications and measurements.squin.gate, which defines a set of gates that can be applied to qubits.squin.noise, which defines noise channels applied to qubits.
Standard library for gate applications
Section titled “Standard library for gate applications”Gates are exported using a standard library directly under the squin namespace.
This allows you to write programs in a concise way.
Here’s a short example:
from bloqade import squin
@squin.kerneldef main(): q = squin.qalloc(2) squin.h(q[0]) squin.cx(q[0], q[1]) return squin.broadcast.measure(q)
# have a look at the IRmain.print()The resulting IR looks like this:

As you can see, calls such as squin.h(q[0]) are lowered as func.invoke statements, i.e. to function calls into the standard library of SQUIN.
For a complete list of all available gates, please see the API reference.
Using control flow
Section titled “Using control flow”One of the central aspects of SQUIN is that you are also able to use standard control flow such as for loops.
For example, we can generalize the two-qubit GHZ kernel function from above to an arbitrary numbers of qubits:
from bloqade import squin
@squin.kerneldef ghz(n: int): q = squin.qalloc(n)
squin.h(q[0]) for i in range(n - 1): squin.cx(q[i], q[i + 1])Note that the fact that gate applications are represented by func.invoke also shows that it’s possible to call user-defined kernel functions in SQUIN!
For example, we could split the above program into two steps
from bloqade import squinfrom bloqade.types import Qubit
from kirin.dialects import ilistfrom typing import Any
@squin.kerneldef allocate_qubits_for_ghz(n: int) -> ilist.IList[Qubit, Any]: q = squin.qalloc(n) squin.h(q[0]) return q
@squin.kerneldef ghz_split(n: int): q = allocate_qubits_for_ghz(n) for i in range(n - 1): squin.cx(q[i], q[i + 1])The squin dialect also includes noise, with a fixed set of noise channels defined.
Just like gates, they are exported under the squin namespace.
For example, we can use this to add noise into the simple kernel from before, which entangles two qubits:
from bloqade import squin
@squin.kerneldef main_noisy(): q = squin.qalloc(2)
squin.h(q[0]) squin.depolarize(p=0.1, qubit=q[0])
squin.cx(q[0], q[1]) squin.depolarize2(0.05, q[0], q[1])
return squin.broadcast.measure(q)
# have a look at the IRmain_noisy.print()The result looks like this:

Note, that you could equivalently write the depolarization error in the above as
dpl = squin.noise.depolarize(p=0.1)squin.qubit.apply(dpl, q[0])A full list of available noise channels can be found in the API reference.
Parallelizing gate applications
Section titled “Parallelizing gate applications”There is also a standard library available for broadcasting gates, i.e. applying a gate to multiple qubits in parallel. For example, the following kernel functions apply the same operations to qubits:
from bloqade import squin
@squin.kerneldef sequential(): q = squin.qalloc(2) squin.h(q[0]) squin.h(q[1])
@squin.kerneldef parallel(): q = squin.qalloc(2) squin.broadcast.h(q)Note that noise can also be parallelized, e.g. by calling squin.broadcast.depolarize(0.1, q).
See the API reference for broadcast for all the available functionality. Note that it will be precisely the same functions as for the standard gate application, but applied to lists of qubits rather than single ones.