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

noise

stmts

Depolarize

Bases: NoiseChannel

Apply n-qubit depolaize error to qubits NOTE For Stim, this can only accept 1 or 2 qubits

PPError

Bases: NoiseChannel

Pauli Product Error

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.

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

ShiftOp

Bases: PrimitiveOp

A phase shift operator.

\[ 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} \]

traits

HasSites dataclass

HasSites()

Bases: StmtTrait

An operator with a sites attribute.

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: IList[Qubit, Any] | list[Qubit]
) -> None

Apply an operator to a list of qubits.

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

Parameters:

Name Type Description Default
operator Op

The operator to apply.

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

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

required

Returns:

Type Description
None

None

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/qubit.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
@wraps(Apply)
def apply(operator: Op, qubits: ilist.IList[Qubit, Any] | list[Qubit]) -> None:
    """Apply an operator to a list of qubits.

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

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

    Returns:
        None
    """
    ...

broadcast

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

Broadcast and apply an operator to a list of qubits. For example, an operator that expects 2 qubits can be applied to a list of 2n qubits, where n is an integer > 0.

For controlled operators, the list of qubits is interpreted as sets of (controls, targets). For example

apply(CX, [q0, q1, q2, q3])

is equivalent to

apply(CX, [q0, q1])
apply(CX, [q2, q3])

Parameters:

Name Type Description Default
operator Op

The operator to broadcast and apply.

required
qubits IList[Qubit, Any] | 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.

required

Returns:

Type Description
None

None

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/qubit.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@wraps(Broadcast)
def broadcast(operator: Op, qubits: ilist.IList[Qubit, Any] | list[Qubit]) -> None:
    """Broadcast and apply an operator to a list of qubits. For example, an operator
    that expects 2 qubits can be applied to a list of 2n qubits, where n is an integer > 0.

    For controlled operators, the list of qubits is interpreted as sets of (controls, targets).
    For example

    ```
    apply(CX, [q0, q1, q2, q3])
    ```

    is equivalent to

    ```
    apply(CX, [q0, q1])
    apply(CX, [q2, q3])
    ```

    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) -> bool
measure(
    input: IList[Qubit, Any] | list[Qubit],
) -> ilist.IList[bool, 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
121
122
123
124
125
126
127
128
129
130
131
132
133
@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.
    """
    ...

measure_and_reset

measure_and_reset(
    qubits: IList[Qubit, Any],
) -> ilist.IList[bool, Any]

Measure the qubits in the list and reset them."

Parameters:

Name Type Description Default
qubits IList[Qubit, Any]

The list of qubits to measure and reset.

required

Returns:

Type Description
IList[bool, Any]

list[bool]: The result of the measurement.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/qubit.py
166
167
168
169
170
171
172
173
174
175
176
@wraps(MeasureAndReset)
def measure_and_reset(qubits: ilist.IList[Qubit, Any]) -> ilist.IList[bool, Any]:
    """Measure the qubits in the list and reset them."

    Args:
        qubits: The list of qubits to measure and reset.

    Returns:
        list[bool]: The result of the measurement.
    """
    ...

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
85
86
87
88
89
90
91
92
93
94
95
@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.
    """
    ...

reset

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

Reset the qubits in the list."

Parameters:

Name Type Description Default
qubits IList[Qubit, Any]

The list of qubits to reset.

required
Source code in .venv/lib/python3.12/site-packages/bloqade/squin/qubit.py
179
180
181
182
183
184
185
186
@wraps(Reset)
def reset(qubits: ilist.IList[Qubit, Any]) -> None:
    """Reset the qubits in the list."

    Args:
        qubits: The list of qubits to reset.
    """
    ...

rewrite

measure_desugar

MeasureDesugarRule

Bases: RewriteRule

Desugar measure operations in the circuit.

qubit_to_stim

SquinQubitToStim

Bases: RewriteRule

rewrite_Apply_and_Broadcast

rewrite_Apply_and_Broadcast(
    stmt: Apply | Broadcast,
) -> RewriteResult

Rewrite Apply and Broadcast nodes to their stim equivalent statements.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/qubit_to_stim.py
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
def rewrite_Apply_and_Broadcast(
    self, stmt: qubit.Apply | qubit.Broadcast
) -> RewriteResult:
    """
    Rewrite Apply and Broadcast nodes to their stim equivalent statements.
    """

    # this is an SSAValue, need it to be the actual operator
    applied_op = stmt.operator.owner
    assert isinstance(applied_op, op.stmts.Operator)

    if isinstance(applied_op, op.stmts.Control):
        return rewrite_Control(stmt)

    # need to handle Control through separate means
    # but we can handle X, Y, Z, H, and S here just fine
    stim_1q_op = SQUIN_STIM_GATE_MAPPING.get(type(applied_op))
    if stim_1q_op is None:
        return RewriteResult()

    address_attr = stmt.qubits.hints.get("address")
    if address_attr is None:
        return RewriteResult()

    assert isinstance(address_attr, AddressAttribute)
    qubit_idx_ssas = insert_qubit_idx_from_address(
        address=address_attr, stmt_to_insert_before=stmt
    )

    if qubit_idx_ssas is None:
        return RewriteResult()

    stim_1q_stmt = stim_1q_op(targets=tuple(qubit_idx_ssas))
    stmt.replace_by(stim_1q_stmt)

    return RewriteResult(has_done_something=True)

squin_measure

SquinMeasureToStim

Bases: RewriteRule

Rewrite squin measure-related statements to stim statements.

get_qubit_idx_ssas

get_qubit_idx_ssas(
    measure_stmt: MeasureQubit | MeasureQubitList | Measure,
) -> tuple[ir.SSAValue, ...] | None

Extract the address attribute and insert qubit indices for the given measure statement.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/squin_measure.py
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
def get_qubit_idx_ssas(
    self, measure_stmt: qubit.MeasureQubit | qubit.MeasureQubitList | wire.Measure
) -> tuple[ir.SSAValue, ...] | None:
    """
    Extract the address attribute and insert qubit indices for the given measure statement.
    """
    match measure_stmt:
        case qubit.MeasureQubit():
            address_attr = measure_stmt.qubit.hints.get("address")
        case qubit.MeasureQubitList():
            address_attr = measure_stmt.qubits.hints.get("address")
        case wire.Measure():
            address_attr = measure_stmt.wire.hints.get("address")
        case _:
            return None

    if address_attr is None:
        return None

    assert isinstance(address_attr, AddressAttribute)

    qubit_idx_ssas = insert_qubit_idx_from_address(
        address=address_attr, stmt_to_insert_before=measure_stmt
    )

    return qubit_idx_ssas

stim_rewrite_util

insert_qubit_idx_after_apply

insert_qubit_idx_after_apply(
    stmt: Apply | Apply | Broadcast | Broadcast,
) -> tuple[ir.SSAValue, ...] | None

Extract qubit indices from Apply or Broadcast statements.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/stim_rewrite_util.py
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def insert_qubit_idx_after_apply(
    stmt: wire.Apply | qubit.Apply | wire.Broadcast | qubit.Broadcast,
) -> tuple[ir.SSAValue, ...] | None:
    """
    Extract qubit indices from Apply or Broadcast statements.
    """
    if isinstance(stmt, (qubit.Apply, qubit.Broadcast)):
        qubits = stmt.qubits
        address_attribute = qubits.hints.get("address")
        if address_attribute is None:
            return
        assert isinstance(address_attribute, AddressAttribute)
        return insert_qubit_idx_from_address(
            address=address_attribute, stmt_to_insert_before=stmt
        )
    elif isinstance(stmt, (wire.Apply, wire.Broadcast)):
        wire_ssas = stmt.inputs
        return insert_qubit_idx_from_wire_ssa(
            wire_ssas=wire_ssas, stmt_to_insert_before=stmt
        )

insert_qubit_idx_from_address

insert_qubit_idx_from_address(
    address: AddressAttribute,
    stmt_to_insert_before: Statement,
) -> tuple[ir.SSAValue, ...] | None

Extract qubit indices from an AddressAttribute and insert them into the SSA form.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/stim_rewrite_util.py
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
def insert_qubit_idx_from_address(
    address: AddressAttribute, stmt_to_insert_before: ir.Statement
) -> tuple[ir.SSAValue, ...] | None:
    """
    Extract qubit indices from an AddressAttribute and insert them into the SSA form.
    """
    address_data = address.address
    qubit_idx_ssas = []

    if isinstance(address_data, AddressTuple):
        for address_qubit in address_data.data:
            if not isinstance(address_qubit, AddressQubit):
                return
            qubit_idx = address_qubit.data
            qubit_idx_stmt = py.Constant(qubit_idx)
            qubit_idx_stmt.insert_before(stmt_to_insert_before)
            qubit_idx_ssas.append(qubit_idx_stmt.result)
    elif isinstance(address_data, AddressWire):
        address_qubit = address_data.origin_qubit
        qubit_idx = address_qubit.data
        qubit_idx_stmt = py.Constant(qubit_idx)
        qubit_idx_stmt.insert_before(stmt_to_insert_before)
        qubit_idx_ssas.append(qubit_idx_stmt.result)
    else:
        return

    return tuple(qubit_idx_ssas)

insert_qubit_idx_from_wire_ssa

insert_qubit_idx_from_wire_ssa(
    wire_ssas: tuple[SSAValue, ...],
    stmt_to_insert_before: Statement,
) -> tuple[ir.SSAValue, ...] | None

Extract qubit indices from wire SSA values and insert them into the SSA form.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/stim_rewrite_util.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def insert_qubit_idx_from_wire_ssa(
    wire_ssas: tuple[ir.SSAValue, ...], stmt_to_insert_before: ir.Statement
) -> tuple[ir.SSAValue, ...] | None:
    """
    Extract qubit indices from wire SSA values and insert them into the SSA form.
    """
    qubit_idx_ssas = []
    for wire_ssa in wire_ssas:
        address_attribute = wire_ssa.hints.get("address")
        if address_attribute is None:
            return
        assert isinstance(address_attribute, AddressAttribute)
        wire_address = address_attribute.address
        assert isinstance(wire_address, AddressWire)
        qubit_idx = wire_address.origin_qubit.data
        qubit_idx_stmt = py.Constant(qubit_idx)
        qubit_idx_ssas.append(qubit_idx_stmt.result)
        qubit_idx_stmt.insert_before(stmt_to_insert_before)

    return tuple(qubit_idx_ssas)

is_measure_result_used

is_measure_result_used(
    stmt: (
        MeasureAndReset
        | MeasureQubit
        | MeasureQubitList
        | MeasureAndReset
        | Measure
    ),
) -> bool

Check if the result of a measure statement is used in the program.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/stim_rewrite_util.py
146
147
148
149
150
151
152
153
154
155
156
157
158
def is_measure_result_used(
    stmt: (
        qubit.MeasureAndReset
        | qubit.MeasureQubit
        | qubit.MeasureQubitList
        | wire.MeasureAndReset
        | wire.Measure
    ),
) -> bool:
    """
    Check if the result of a measure statement is used in the program.
    """
    return bool(stmt.result.uses)

rewrite_Control

rewrite_Control(
    stmt_with_ctrl: Apply | Apply | Broadcast | Broadcast,
) -> RewriteResult

Handle control gates for Apply and Broadcast statements.

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/stim_rewrite_util.py
 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
def rewrite_Control(
    stmt_with_ctrl: qubit.Apply | wire.Apply | qubit.Broadcast | wire.Broadcast,
) -> RewriteResult:
    """
    Handle control gates for Apply and Broadcast statements.
    """
    ctrl_op = stmt_with_ctrl.operator.owner
    assert isinstance(ctrl_op, op.stmts.Control)

    ctrl_op_target_gate = ctrl_op.op.owner
    assert isinstance(ctrl_op_target_gate, op.stmts.Operator)

    qubit_idx_ssas = insert_qubit_idx_after_apply(stmt=stmt_with_ctrl)
    if qubit_idx_ssas is None:
        return RewriteResult()

    # Separate control and target qubits
    target_qubits = []
    ctrl_qubits = []
    for i in range(len(qubit_idx_ssas)):
        if (i % 2) == 0:
            ctrl_qubits.append(qubit_idx_ssas[i])
        else:
            target_qubits.append(qubit_idx_ssas[i])

    target_qubits = tuple(target_qubits)
    ctrl_qubits = tuple(ctrl_qubits)

    supported_gate_mapping = {
        op.stmts.X: gate.CX,
        op.stmts.Y: gate.CY,
        op.stmts.Z: gate.CZ,
    }

    stim_gate = supported_gate_mapping.get(type(ctrl_op_target_gate))
    if stim_gate is None:
        return RewriteResult()

    stim_stmt = stim_gate(controls=ctrl_qubits, targets=target_qubits)

    if isinstance(stmt_with_ctrl, (wire.Apply, wire.Broadcast)):
        # have to "reroute" the input of these statements to directly plug in
        # to subsequent statements, remove dependency on the current statement
        for input_wire, output_wire in zip(
            stmt_with_ctrl.inputs, stmt_with_ctrl.results
        ):
            output_wire.replace_by(input_wire)

    stmt_with_ctrl.replace_by(stim_stmt)

    return RewriteResult(has_done_something=True)

wire_identity_elimination

SquinWireIdentityElimination

Bases: RewriteRule

rewrite_Statement

rewrite_Statement(node: Statement) -> RewriteResult

Handle the case where an unwrap feeds a wire directly into a wrap, equivalent to nothing happening/identity operation

w = unwrap(qubit) wrap(qubit, w)

Source code in .venv/lib/python3.12/site-packages/bloqade/squin/rewrite/wire_identity_elimination.py
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def rewrite_Statement(self, node: ir.Statement) -> RewriteResult:
    """
    Handle the case where an unwrap feeds a wire directly into a wrap,
    equivalent to nothing happening/identity operation

    w = unwrap(qubit)
    wrap(qubit, w)
    """
    if isinstance(node, wire.Wrap):
        wire_origin_stmt = node.wire.owner
        if isinstance(wire_origin_stmt, wire.Unwrap):
            node.delete()  # get rid of wrap
            wire_origin_stmt.delete()  # get rid of the unwrap
            return RewriteResult(has_done_something=True)

    return RewriteResult()

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.