Skip to content

dialects

auxiliary

stmts

const

ConstBool

Bases: Statement

IR Statement representing a constant boolean value.

result class-attribute instance-attribute
result: ResultValue = result(Bool)

result (Float): The result value.

value class-attribute instance-attribute
value: bool = attribute(Bool)

value (float): The constant float value.

ConstFloat

Bases: Statement

IR Statement representing a constant float value.

result class-attribute instance-attribute
result: ResultValue = result(Float)

result (Float): The result value.

value class-attribute instance-attribute
value: float = attribute(Float)

value (float): The constant float value.

ConstInt

Bases: Statement

IR Statement representing a constant integer value.

result class-attribute instance-attribute
result: ResultValue = result(Int)

result (Int): The result value.

value class-attribute instance-attribute
value: int = attribute(Int)

value (int): The constant integer value.

ConstStr

Bases: Statement

IR Statement representing a constant str value.

result class-attribute instance-attribute
result: ResultValue = result(String)

result (str): The result value.

value class-attribute instance-attribute
value: str = attribute(String)

value (str): The constant str value.

Neg

Bases: Statement

IR Statement representing a negation operation.

collapse

stmts

measure

Measurement

Bases: Statement

p class-attribute instance-attribute
p: SSAValue = argument(Float)

probability of noise introduced by measurement. For example 0.01 means 1% the measurement will be flipped

pp_measure

PPMeasurement

Bases: Statement

p class-attribute instance-attribute
p: SSAValue = argument(Float)

probability of noise introduced by measurement. For example 0.01 means 1% the measurement will be flipped

parse

lowering

One-to-one lowering routine from stim circuit to a stim-dialect kirin kernel.

loads

loads(
    stim_str: str,
    *,
    kernel_name: str = "main",
    ignore_unknown_stim: bool = False,
    error_unknown_nonstim: bool = False,
    nonstim_noise_ops: dict[str, type[Statement]] = {},
    dialects: DialectGroup | None = None,
    globals: dict[str, Any] | None = None,
    file: str | None = None,
    lineno_offset: int = 0,
    col_offset: int = 0,
    compactify: bool = True
) -> ir.Method[[], None]

Loads a STIM string and returns the corresponding kernel object.

Parameters:

Name Type Description Default
stim_str str

The string representation of a STIM circuit to load.

required

Other Parameters:

Name Type Description
kernel_name str

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

ignore_unknown_stim bool

If True, don't throw a build error on an unimplemented stim instruction.

error_unknown_nonstim bool

If True, throw a build error if an unknown tag is used on the I_ERROR instruction.

nonstim_noise_ops dict[str, Statement]

Additional statements to represent non-standard stim operations. The dictionary key should match the tag used to identify it in stim (stim format I_ERROR[MY_NOISE](0.05) 0 1 2 3 or I_ERROR[MY_CORRELATED_NOISE:2417696374](0.03) 1 3 5).

dialects DialectGroup | None

The dialects to use. Defaults to stim.main.

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.

Example:

from bloqade.stim.lowering import loads
method = loads('''
    X 0 2 4
    DEPOLARIZE1(0.01) 0
    I_ERROR[CUSTOM_ERROR](0.02) 2 4
    M 0 2 4
    DETECTOR rec[-1] rec[-2]
''')
Source code in .venv/lib/python3.12/site-packages/bloqade/stim/parse/lowering.py
 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
def loads(
    stim_str: str,
    *,
    kernel_name: str = "main",
    ignore_unknown_stim: bool = False,
    error_unknown_nonstim: bool = False,
    nonstim_noise_ops: dict[str, type[kirin.ir.Statement]] = {},
    dialects: ir.DialectGroup | None = None,
    globals: dict[str, Any] | None = None,
    file: str | None = None,
    lineno_offset: int = 0,
    col_offset: int = 0,
    compactify: bool = True,
) -> ir.Method[[], None]:
    """Loads a STIM string and returns the corresponding kernel object.

    Args:
        stim_str: The string representation of a STIM circuit to load.

    Keyword Args:
        kernel_name (str): The name of the kernel to load. Defaults to "main".
        ignore_unknown_stim (bool): If True, don't throw a build error on an
            unimplemented stim instruction.
        error_unknown_nonstim (bool): If True, throw a build error if an unknown tag is
            used on the `I_ERROR` instruction.
        nonstim_noise_ops (dict[str, kirin.ir.Statement]): Additional statements to
            represent non-standard stim operations.  The dictionary key should match the
            tag used to identify it in stim (stim format
            `I_ERROR[MY_NOISE](0.05) 0 1 2 3` or
            `I_ERROR[MY_CORRELATED_NOISE:2417696374](0.03) 1 3 5`).
        dialects (ir.DialectGroup | None): The dialects to use. Defaults to `stim.main`.
        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.

    Example:

    ```python
    from bloqade.stim.lowering import loads
    method = loads('''
        X 0 2 4
        DEPOLARIZE1(0.01) 0
        I_ERROR[CUSTOM_ERROR](0.02) 2 4
        M 0 2 4
        DETECTOR rec[-1] rec[-2]
    ''')
    ```
    """
    import stim  # Optional dependency required to lower stim circuits

    circ = stim.Circuit(stim_str)
    stim_lowering = Stim(
        kstim.main if dialects is None else dialects,
        ignore_unknown_stim=ignore_unknown_stim,
        error_unknown_nonstim=error_unknown_nonstim,
        nonstim_noise_ops=nonstim_noise_ops,
    )
    frame = stim_lowering.get_frame(
        circ,
        source=stim_str,
        file=file,
        globals=globals,
        lineno_offset=lineno_offset,
        col_offset=col_offset,
        compactify=compactify,
    )

    return_value = func.ConstantNone()  # No return value
    frame.push(return_value)
    return_node = frame.push(func.Return(value_or_stmt=return_value))

    body = frame.curr_region
    code = func.Function(
        sym_name=kernel_name,
        signature=func.Signature((), return_node.value.type),
        body=body,
    )
    return ir.Method(
        mod=None,
        py_func=None,
        sym_name=kernel_name,
        arg_names=[],
        dialects=kstim.dialects,
        code=code,
    )

rewrite

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/stim/rewrite/qubit_to_stim.py
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
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)

    # check if its adjoint, assume its canonicalized so no nested adjoints.
    is_conj = False
    if isinstance(applied_op, op.stmts.Adjoint):
        if not applied_op.is_unitary:
            return RewriteResult()

        is_conj = True
        applied_op = applied_op.op.owner

    # 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()

    # sometimes you can get a whole AddressReg...
    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()

    if isinstance(stim_1q_op, gate.stmts.Gate):
        stim_1q_stmt = stim_1q_op(targets=tuple(qubit_idx_ssas), dagger=is_conj)
    else:
        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/stim/rewrite/squin_measure.py
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
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

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/stim/rewrite/util.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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/stim/rewrite/util.py
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
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
            create_and_insert_qubit_idx_stmt(
                address_qubit.data, stmt_to_insert_before, qubit_idx_ssas
            )
    elif isinstance(address_data, AddressReg):
        for qubit_idx in address_data.data:
            create_and_insert_qubit_idx_stmt(
                qubit_idx, stmt_to_insert_before, qubit_idx_ssas
            )
    elif isinstance(address_data, AddressQubit):
        create_and_insert_qubit_idx_stmt(
            address_data.data, stmt_to_insert_before, qubit_idx_ssas
        )
    elif isinstance(address_data, AddressWire):
        address_qubit = address_data.origin_qubit
        create_and_insert_qubit_idx_stmt(
            address_qubit.data, stmt_to_insert_before, qubit_idx_ssas
        )
    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/stim/rewrite/util.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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: MeasureQubit | MeasureQubitList | 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/stim/rewrite/util.py
166
167
168
169
170
171
172
def is_measure_result_used(
    stmt: qubit.MeasureQubit | qubit.MeasureQubitList | 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/stim/rewrite/util.py
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
159
160
161
162
163
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)

    stim_gate = SQUIN_STIM_CONTROL_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/stim/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()