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

Qiskit / qiskit / 15925725626

27 Jun 2025 11:38AM UTC coverage: 87.676% (-0.3%) from 88.02%
15925725626

push

github

web-flow
Bump indexmap from 2.9.0 to 2.10.0 (#14675)

Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.9.0 to 2.10.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.9.0...2.10.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

80984 of 92367 relevant lines covered (87.68%)

506751.9 hits per line

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

96.26
/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<'py> IntoPyObject<'py> for InternalBytecode {
215
    type Target = Bytecode;
216
    type Output = Bound<'py, Self::Target>;
217
    type Error = PyErr;
218

219
    /// Convert the internal bytecode representation to a Python-space one.
220
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
5,738✔
221
        Bound::new(
5,738✔
222
            py,
5,738✔
223
            match self {
5,738✔
224
                InternalBytecode::Gate {
225
                    id,
2,042✔
226
                    arguments,
2,042✔
227
                    qubits,
2,042✔
228
                } => Bytecode {
229
                    opcode: OpCode::Gate,
2,042✔
230
                    operands: (id, arguments, qubits)
2,042✔
231
                        .into_pyobject(py)?
2,042✔
232
                        .into_any()
2,042✔
233
                        .unbind(),
2,042✔
234
                },
235
                InternalBytecode::ConditionedGate {
236
                    id,
114✔
237
                    arguments,
114✔
238
                    qubits,
114✔
239
                    creg,
114✔
240
                    value,
114✔
241
                } => Bytecode {
242
                    opcode: OpCode::ConditionedGate,
114✔
243
                    operands: (id, arguments, qubits, creg, value)
114✔
244
                        .into_pyobject(py)?
114✔
245
                        .into_any()
114✔
246
                        .unbind(),
114✔
247
                },
248
                InternalBytecode::Measure { qubit, clbit } => Bytecode {
430✔
249
                    opcode: OpCode::Measure,
430✔
250
                    operands: (qubit, clbit).into_pyobject(py)?.into_any().unbind(),
430✔
251
                },
252
                InternalBytecode::ConditionedMeasure {
253
                    qubit,
12✔
254
                    clbit,
12✔
255
                    creg,
12✔
256
                    value,
12✔
257
                } => Bytecode {
258
                    opcode: OpCode::ConditionedMeasure,
12✔
259
                    operands: (qubit, clbit, creg, value)
12✔
260
                        .into_pyobject(py)?
12✔
261
                        .into_any()
12✔
262
                        .unbind(),
12✔
263
                },
264
                InternalBytecode::Reset { qubit } => Bytecode {
6✔
265
                    opcode: OpCode::Reset,
6✔
266
                    operands: (qubit,).into_pyobject(py)?.into_any().unbind(),
6✔
267
                },
268
                InternalBytecode::ConditionedReset { qubit, creg, value } => Bytecode {
12✔
269
                    opcode: OpCode::ConditionedReset,
12✔
270
                    operands: (qubit, creg, value).into_pyobject(py)?.into_any().unbind(),
12✔
271
                },
272
                InternalBytecode::Barrier { qubits } => Bytecode {
154✔
273
                    opcode: OpCode::Barrier,
154✔
274
                    operands: (qubits,).into_pyobject(py)?.into_any().unbind(),
154✔
275
                },
276
                InternalBytecode::DeclareQreg { name, size } => Bytecode {
1,172✔
277
                    opcode: OpCode::DeclareQreg,
1,172✔
278
                    operands: (name, size).into_pyobject(py)?.into_any().unbind(),
1,172✔
279
                },
280
                InternalBytecode::DeclareCreg { name, size } => Bytecode {
830✔
281
                    opcode: OpCode::DeclareCreg,
830✔
282
                    operands: (name, size).into_pyobject(py)?.into_any().unbind(),
830✔
283
                },
284
                InternalBytecode::DeclareGate { name, num_qubits } => Bytecode {
170✔
285
                    opcode: OpCode::DeclareGate,
170✔
286
                    operands: (name, num_qubits).into_pyobject(py)?.into_any().unbind(),
170✔
287
                },
288
                InternalBytecode::GateInBody {
289
                    id,
282✔
290
                    arguments,
282✔
291
                    qubits,
282✔
292
                } => Bytecode {
293
                    // In Python space, we don't have to be worried about the types of the
294
                    // parameters changing here, so we can just use `OpCode::Gate` unlike in the
295
                    // internal bytecode.
296
                    opcode: OpCode::Gate,
282✔
297
                    operands: (id, arguments.into_pyobject(py)?, qubits)
282✔
298
                        .into_pyobject(py)?
282✔
299
                        .into_any()
282✔
300
                        .unbind(),
282✔
301
                },
302
                InternalBytecode::EndDeclareGate {} => Bytecode {
303
                    opcode: OpCode::EndDeclareGate,
170✔
304
                    operands: ().into_pyobject(py)?.into_any().unbind(),
170✔
305
                },
306
                InternalBytecode::DeclareOpaque { name, num_qubits } => Bytecode {
36✔
307
                    opcode: OpCode::DeclareOpaque,
36✔
308
                    operands: (name, num_qubits).into_pyobject(py)?.into_any().unbind(),
36✔
309
                },
310
                InternalBytecode::SpecialInclude { indices } => Bytecode {
308✔
311
                    opcode: OpCode::SpecialInclude,
308✔
312
                    operands: (indices,).into_pyobject(py)?.into_any().unbind(),
308✔
313
                },
314
            },
315
        )
316
    }
5,738✔
317
}
318

319
/// The custom iterator object that is returned up to Python space for iteration through the
320
/// bytecode stream.  This is never constructed on the Python side; it is built in Rust space
321
/// by Python calls to [bytecode_from_string] and [bytecode_from_file].
322
#[pyclass]
323
pub struct BytecodeIterator {
324
    parser_state: parse::State,
325
    buffer: Vec<Option<InternalBytecode>>,
326
    buffer_used: usize,
327
}
328

329
impl BytecodeIterator {
330
    pub fn new(
1,180✔
331
        tokens: lex::TokenStream,
1,180✔
332
        include_path: Vec<std::path::PathBuf>,
1,180✔
333
        custom_instructions: &[CustomInstruction],
1,180✔
334
        custom_classical: &[CustomClassical],
1,180✔
335
        strict: bool,
1,180✔
336
    ) -> PyResult<Self> {
1,180✔
337
        Ok(BytecodeIterator {
338
            parser_state: parse::State::new(
1,180✔
339
                tokens,
1,180✔
340
                include_path,
1,180✔
341
                custom_instructions,
1,180✔
342
                custom_classical,
1,180✔
343
                strict,
1,180✔
344
            )
345
            .map_err(QASM2ParseError::new_err)?,
1,180✔
346
            buffer: vec![],
1,156✔
347
            buffer_used: 0,
348
        })
349
    }
1,180✔
350
}
351

352
#[pymethods]
×
353
impl BytecodeIterator {
354
    fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
2,482✔
355
        slf
2,482✔
356
    }
2,482✔
357

358
    fn __next__(&mut self, py: Python<'_>) -> PyResult<Option<Bytecode>> {
6,890✔
359
        if self.buffer_used >= self.buffer.len() {
6,890✔
360
            self.buffer.clear();
6,094✔
361
            self.buffer_used = 0;
6,094✔
362
            self.parser_state.parse_next(&mut self.buffer)?;
6,094✔
363
        }
796✔
364
        if self.buffer.is_empty() {
6,218✔
365
            Ok(None)
480✔
366
        } else {
367
            self.buffer_used += 1;
5,738✔
368
            Ok(self.buffer[self.buffer_used - 1]
5,738✔
369
                .take()
5,738✔
370
                .map(|bytecode| bytecode.into_pyobject(py))
5,738✔
371
                .transpose()?
5,738✔
372
                .map(|x| x.get().clone()))
5,738✔
373
        }
374
    }
6,890✔
375
}
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