Structural Quantum Instructions dialect
This 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
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
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.kernel
def main():
q = squin.qalloc(2)
squin.h(q[0])
squin.cx(q[0], q[1])
return squin.qubit.measure(q)
# have a look at the IR
main.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
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.kernel
def 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 squin
from bloqade.types import Qubit
from kirin.dialects import ilist
from typing import Any
@squin.kernel
def allocate_qubits_for_ghz(n: int) -> ilist.IList[Qubit, Any]:
q = squin.qalloc(n)
squin.h(q[0])
return q
@squin.kernel
def ghz_split(n: int):
q = allocate_qubits_for_ghz(n)
for i in range(n - 1):
squin.cx(q[i], q[i + 1])
Noise
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.kernel
def 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.qubit.measure(q)
# have a look at the IR
main_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
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.kernel
def sequential():
q = squin.qalloc(2)
squin.h(q[0])
squin.h(q[1])
@squin.kernel
def 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.