• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Qiskit / qiskit / 16975764228

14 Aug 2025 07:43PM UTC coverage: 88.519% (-0.3%) from 88.814%
16975764228

push

github

web-flow
Fixing failing CI tests after Rustworkx 0.17 release (#14888) (#14914)

* fixing failing CI tests

* lint

(cherry picked from commit 6fbe13e3f)

Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>

77336 of 87367 relevant lines covered (88.52%)

356058.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.46
/crates/qasm2/src/bytecode.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2023
4
//
5
// This code is licensed under the Apache License, Version 2.0. You may
6
// obtain a copy of this license in the LICENSE.txt file in the root directory
7
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
//
9
// Any modifications or derivative works of this code must retain this
10
// copyright notice, and modified files need to carry a notice indicating
11
// that they have been altered from the originals.
12

13
use num_bigint::BigUint;
14
use pyo3::prelude::*;
15

16
use crate::error::QASM2ParseError;
17
use crate::expr::Expr;
18
use crate::lex;
19
use crate::parse;
20
use crate::parse::{ClbitId, CregId, GateId, ParamId, QubitId};
21
use crate::{CustomClassical, CustomInstruction};
22

23
/// The Rust parser produces an iterator of these `Bytecode` instructions, which comprise an opcode
24
/// integer for operation distinction, and a free-form tuple containing the operands.
25
#[pyclass(module = "qiskit._accelerate.qasm2", frozen)]
26
#[derive(Clone)]
27
pub struct Bytecode {
28
    #[pyo3(get)]
29
    opcode: OpCode,
30
    #[pyo3(get)]
31
    operands: PyObject,
32
}
33

34
/// The operations that are represented by the "bytecode" passed to Python.
35
#[pyclass(module = "qiskit._accelerate.qasm2", frozen, eq)]
×
36
#[derive(Clone, Eq, PartialEq)]
37
pub enum OpCode {
38
    // There is only a `Gate` here, not a `GateInBasis`, because in Python space we don't have the
39
    // same strict typing requirements to satisfy.
40
    Gate,
41
    ConditionedGate,
42
    Measure,
43
    ConditionedMeasure,
44
    Reset,
45
    ConditionedReset,
46
    Barrier,
47
    DeclareQreg,
48
    DeclareCreg,
49
    DeclareGate,
50
    EndDeclareGate,
51
    DeclareOpaque,
52
    SpecialInclude,
53
}
54

55
// The following structs, with `Expr` or `OpCode` in the name (but not the top-level `OpCode`
56
// above) build up the tree of symbolic expressions for the parameter applications within gate
57
// bodies.  We choose to store this in the gate classes that the Python component emits, so it can
58
// lazily create definitions as required, rather than eagerly binding them as the file is parsed.
59
//
60
// In Python space we would usually have the classes inherit from some shared subclass, but doing
61
// that makes things a little fiddlier with PyO3, and there's no real benefit for our uses.
62

63
/// A (potentially folded) floating-point constant value as part of an expression.
64
#[pyclass(module = "qiskit._accelerate.qasm2", frozen)]
65
#[derive(Clone)]
66
pub struct ExprConstant {
67
    #[pyo3(get)]
68
    pub value: f64,
69
}
70

71
/// A reference to one of the arguments to the gate.
72
#[pyclass(module = "qiskit._accelerate.qasm2", frozen)]
73
#[derive(Clone)]
74
pub struct ExprArgument {
75
    #[pyo3(get)]
76
    pub index: ParamId,
77
}
78

79
/// A unary operation acting on some other part of the expression tree.  This includes the `+` and
80
/// `-` unary operators, but also any of the built-in scientific-calculator functions.
81
#[pyclass(module = "qiskit._accelerate.qasm2", frozen)]
82
#[derive(Clone)]
83
pub struct ExprUnary {
84
    #[pyo3(get)]
85
    pub opcode: UnaryOpCode,
86
    #[pyo3(get)]
87
    pub argument: PyObject,
88
}
89

90
/// A binary operation acting on two other parts of the expression tree.
91
#[pyclass(module = "qiskit._accelerate.qasm2", frozen)]
92
#[derive(Clone)]
93
pub struct ExprBinary {
94
    #[pyo3(get)]
95
    pub opcode: BinaryOpCode,
96
    #[pyo3(get)]
97
    pub left: PyObject,
98
    #[pyo3(get)]
99
    pub right: PyObject,
100
}
101

102
/// Some custom callable Python function that the user told us about.
103
#[pyclass(module = "qiskit._accelerate.qasm2", frozen)]
104
#[derive(Clone)]
105
pub struct ExprCustom {
106
    #[pyo3(get)]
107
    pub callable: PyObject,
108
    #[pyo3(get)]
109
    pub arguments: Vec<PyObject>,
110
}
111

112
/// Discriminator for the different types of unary operator.  We could have a separate class for
113
/// each of these, but this way involves fewer imports in Python, and also serves to split up the
114
/// option tree at the top level, so we don't have to test every unary operator before testing
115
/// other operations.
116
#[pyclass(module = "qiskit._accelerate.qasm2", frozen, eq)]
×
117
#[derive(Clone, PartialEq, Eq)]
118
pub enum UnaryOpCode {
119
    Negate,
120
    Cos,
121
    Exp,
122
    Ln,
123
    Sin,
124
    Sqrt,
125
    Tan,
126
}
127

128
/// Discriminator for the different types of binary operator.  We could have a separate class for
129
/// each of these, but this way involves fewer imports in Python, and also serves to split up the
130
/// option tree at the top level, so we don't have to test every binary operator before testing
131
/// other operations.
132
#[pyclass(module = "qiskit._accelerate.qasm2", frozen, eq)]
×
133
#[derive(Clone, PartialEq, Eq)]
134
pub enum BinaryOpCode {
135
    Add,
136
    Subtract,
137
    Multiply,
138
    Divide,
139
    Power,
140
}
141

142
/// An internal representation of the bytecode that will later be converted to the more free-form
143
/// [Bytecode] Python-space objects.  This is fairly tightly coupled to Python space; the intent is
144
/// just to communicate to Python as concisely as possible what it needs to do.  We want to have as
145
/// little work to do in Python space as possible, since everything is slower there.
146
///
147
/// In various enumeration items, we use zero-indexed numeric keys to identify the object rather
148
/// than its name.  This is much more efficient in Python-space; rather than needing to build and
149
/// lookup things in a hashmap, we can just build Python lists and index them directly, which also
150
/// has the advantage of not needing to pass strings to Python for each gate.  It also gives us
151
/// consistency with how qubits and clbits are tracked; there is no need to track both the register
152
/// name and the index separately when we can use a simple single index.
153
pub enum InternalBytecode {
154
    Gate {
155
        id: GateId,
156
        arguments: Vec<f64>,
157
        qubits: Vec<QubitId>,
158
    },
159
    ConditionedGate {
160
        id: GateId,
161
        arguments: Vec<f64>,
162
        qubits: Vec<QubitId>,
163
        creg: CregId,
164
        value: BigUint,
165
    },
166
    Measure {
167
        qubit: QubitId,
168
        clbit: ClbitId,
169
    },
170
    ConditionedMeasure {
171
        qubit: QubitId,
172
        clbit: ClbitId,
173
        creg: CregId,
174
        value: BigUint,
175
    },
176
    Reset {
177
        qubit: QubitId,
178
    },
179
    ConditionedReset {
180
        qubit: QubitId,
181
        creg: CregId,
182
        value: BigUint,
183
    },
184
    Barrier {
185
        qubits: Vec<QubitId>,
186
    },
187
    DeclareQreg {
188
        name: String,
189
        size: usize,
190
    },
191
    DeclareCreg {
192
        name: String,
193
        size: usize,
194
    },
195
    DeclareGate {
196
        name: String,
197
        num_qubits: usize,
198
    },
199
    GateInBody {
200
        id: GateId,
201
        arguments: Vec<Expr>,
202
        qubits: Vec<QubitId>,
203
    },
204
    EndDeclareGate {},
205
    DeclareOpaque {
206
        name: String,
207
        num_qubits: usize,
208
    },
209
    SpecialInclude {
210
        indices: Vec<usize>,
211
    },
212
}
213

214
impl IntoPy<Bytecode> for InternalBytecode {
215
    /// Convert the internal bytecode representation to a Python-space one.
216
    fn into_py(self, py: Python<'_>) -> Bytecode {
6,072✔
217
        match self {
6,072✔
218
            InternalBytecode::Gate {
219
                id,
2,094✔
220
                arguments,
2,094✔
221
                qubits,
2,094✔
222
            } => Bytecode {
2,094✔
223
                opcode: OpCode::Gate,
2,094✔
224
                operands: (id, arguments, qubits).into_py(py),
2,094✔
225
            },
2,094✔
226
            InternalBytecode::ConditionedGate {
227
                id,
168✔
228
                arguments,
168✔
229
                qubits,
168✔
230
                creg,
168✔
231
                value,
168✔
232
            } => Bytecode {
168✔
233
                opcode: OpCode::ConditionedGate,
168✔
234
                operands: (id, arguments, qubits, creg, value).into_py(py),
168✔
235
            },
168✔
236
            InternalBytecode::Measure { qubit, clbit } => Bytecode {
488✔
237
                opcode: OpCode::Measure,
488✔
238
                operands: (qubit, clbit).into_py(py),
488✔
239
            },
488✔
240
            InternalBytecode::ConditionedMeasure {
241
                qubit,
12✔
242
                clbit,
12✔
243
                creg,
12✔
244
                value,
12✔
245
            } => Bytecode {
12✔
246
                opcode: OpCode::ConditionedMeasure,
12✔
247
                operands: (qubit, clbit, creg, value).into_py(py),
12✔
248
            },
12✔
249
            InternalBytecode::Reset { qubit } => Bytecode {
6✔
250
                opcode: OpCode::Reset,
6✔
251
                operands: (qubit,).into_py(py),
6✔
252
            },
6✔
253
            InternalBytecode::ConditionedReset { qubit, creg, value } => Bytecode {
12✔
254
                opcode: OpCode::ConditionedReset,
12✔
255
                operands: (qubit, creg, value).into_py(py),
12✔
256
            },
12✔
257
            InternalBytecode::Barrier { qubits } => Bytecode {
160✔
258
                opcode: OpCode::Barrier,
160✔
259
                operands: (qubits,).into_py(py),
160✔
260
            },
160✔
261
            InternalBytecode::DeclareQreg { name, size } => Bytecode {
1,226✔
262
                opcode: OpCode::DeclareQreg,
1,226✔
263
                operands: (name, size).into_py(py),
1,226✔
264
            },
1,226✔
265
            InternalBytecode::DeclareCreg { name, size } => Bytecode {
920✔
266
                opcode: OpCode::DeclareCreg,
920✔
267
                operands: (name, size).into_py(py),
920✔
268
            },
920✔
269
            InternalBytecode::DeclareGate { name, num_qubits } => Bytecode {
168✔
270
                opcode: OpCode::DeclareGate,
168✔
271
                operands: (name, num_qubits).into_py(py),
168✔
272
            },
168✔
273
            InternalBytecode::GateInBody {
274
                id,
268✔
275
                arguments,
268✔
276
                qubits,
268✔
277
            } => Bytecode {
268✔
278
                // In Python space, we don't have to be worried about the types of the
268✔
279
                // parameters changing here, so we can just use `OpCode::Gate` unlike in the
268✔
280
                // internal bytecode.
268✔
281
                opcode: OpCode::Gate,
268✔
282
                operands: (id, arguments.into_py(py), qubits).into_py(py),
268✔
283
            },
268✔
284
            InternalBytecode::EndDeclareGate {} => Bytecode {
168✔
285
                opcode: OpCode::EndDeclareGate,
168✔
286
                operands: ().into_py(py),
168✔
287
            },
168✔
288
            InternalBytecode::DeclareOpaque { name, num_qubits } => Bytecode {
36✔
289
                opcode: OpCode::DeclareOpaque,
36✔
290
                operands: (name, num_qubits).into_py(py),
36✔
291
            },
36✔
292
            InternalBytecode::SpecialInclude { indices } => Bytecode {
346✔
293
                opcode: OpCode::SpecialInclude,
346✔
294
                operands: (indices,).into_py(py),
346✔
295
            },
346✔
296
        }
297
    }
6,072✔
298
}
299

300
/// The custom iterator object that is returned up to Python space for iteration through the
301
/// bytecode stream.  This is never constructed on the Python side; it is built in Rust space
302
/// by Python calls to [bytecode_from_string] and [bytecode_from_file].
303
#[pyclass]
304
pub struct BytecodeIterator {
305
    parser_state: parse::State,
306
    buffer: Vec<Option<InternalBytecode>>,
307
    buffer_used: usize,
308
}
309

310
impl BytecodeIterator {
311
    pub fn new(
1,230✔
312
        tokens: lex::TokenStream,
1,230✔
313
        include_path: Vec<std::path::PathBuf>,
1,230✔
314
        custom_instructions: &[CustomInstruction],
1,230✔
315
        custom_classical: &[CustomClassical],
1,230✔
316
        strict: bool,
1,230✔
317
    ) -> PyResult<Self> {
1,230✔
318
        Ok(BytecodeIterator {
319
            parser_state: parse::State::new(
1,230✔
320
                tokens,
1,230✔
321
                include_path,
1,230✔
322
                custom_instructions,
1,230✔
323
                custom_classical,
1,230✔
324
                strict,
1,230✔
325
            )
326
            .map_err(QASM2ParseError::new_err)?,
1,230✔
327
            buffer: vec![],
1,206✔
328
            buffer_used: 0,
329
        })
330
    }
1,230✔
331
}
332

333
#[pymethods]
×
334
impl BytecodeIterator {
335
    fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
2,580✔
336
        slf
2,580✔
337
    }
2,580✔
338

339
    fn __next__(&mut self, py: Python<'_>) -> PyResult<Option<Bytecode>> {
7,274✔
340
        if self.buffer_used >= self.buffer.len() {
7,274✔
341
            self.buffer.clear();
6,446✔
342
            self.buffer_used = 0;
6,446✔
343
            self.parser_state.parse_next(&mut self.buffer)?;
6,446✔
344
        }
828✔
345
        if self.buffer.is_empty() {
6,590✔
346
            Ok(None)
518✔
347
        } else {
348
            self.buffer_used += 1;
6,072✔
349
            Ok(self.buffer[self.buffer_used - 1]
6,072✔
350
                .take()
6,072✔
351
                .map(|bytecode| bytecode.into_py(py)))
6,072✔
352
        }
353
    }
7,274✔
354
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc