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

Qiskit / qiskit / 21130221909

19 Jan 2026 08:21AM UTC coverage: 87.946% (-0.4%) from 88.317%
21130221909

Pull #15561

github

web-flow
Merge 78c2f3255 into 654cb87bd
Pull Request #15561: Evolve SparseObservable

141 of 149 new or added lines in 3 files covered. (94.63%)

1703 existing lines in 25 files now uncovered.

100323 of 114074 relevant lines covered (87.95%)

1157252.47 hits per line

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

98.73
/crates/circuit/src/classical/expr/binary.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2025
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 crate::classical::expr::{Expr, ExprKind, PyExpr};
14
use crate::classical::types::Type;
15
use crate::imports;
16
use pyo3::prelude::*;
17
use pyo3::types::PyTuple;
18
use pyo3::{IntoPyObjectExt, intern};
19

20
/// A binary expression.
21
#[derive(Clone, Debug, PartialEq)]
22
pub struct Binary {
23
    pub op: BinaryOp,
24
    pub left: Expr,
25
    pub right: Expr,
26
    pub ty: Type,
27
    pub constant: bool,
28
}
29

30
/// The Rust-side enum indicating a [Binary] expression's kind.
31
///
32
/// The values are part of the public Qiskit Python interface, since
33
/// they are public in the sister Python enum `_BinaryOp` in `expr.py`
34
/// and used in our QPY serialization format.
35
///
36
/// WARNING: If you add more, **be sure to update expr.py** as well
37
/// as the implementation of [::bytemuck::CheckedBitPattern]
38
/// below.
39
#[repr(u8)]
40
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
41
pub enum BinaryOp {
42
    BitAnd = 1,
43
    BitOr = 2,
44
    BitXor = 3,
45
    LogicAnd = 4,
46
    LogicOr = 5,
47
    Equal = 6,
48
    NotEqual = 7,
49
    Less = 8,
50
    LessEqual = 9,
51
    Greater = 10,
52
    GreaterEqual = 11,
53
    ShiftLeft = 12,
54
    ShiftRight = 13,
55
    Add = 14,
56
    Sub = 15,
57
    Mul = 16,
58
    Div = 17,
59
}
60

61
unsafe impl ::bytemuck::CheckedBitPattern for BinaryOp {
62
    type Bits = u8;
63

64
    fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
3,022✔
65
        *bits > 0 && *bits < 18
3,022✔
66
    }
3,022✔
67
}
68

69
impl BinaryOp {
70
    pub fn from_u8(value: u8) -> PyResult<BinaryOp> {
298✔
71
        Ok(bytemuck::checked::cast::<u8, BinaryOp>(value))
298✔
72
    }
298✔
73
}
74

75
impl<'py> IntoPyObject<'py> for Binary {
76
    type Target = PyAny;
77
    type Output = Bound<'py, PyAny>;
78
    type Error = PyErr;
79

80
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
2,228✔
81
        Ok(Bound::new(py, (PyBinary(self), PyExpr(ExprKind::Binary)))?.into_any())
2,228✔
82
    }
2,228✔
83
}
84

85
impl<'a, 'py> FromPyObject<'a, 'py> for Binary {
86
    type Error = <PyBinary as FromPyObject<'a, 'py>>::Error;
87

88
    fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
2,560✔
89
        let PyBinary(b) = ob.extract()?;
2,560✔
90
        Ok(b)
2,560✔
91
    }
2,560✔
92
}
93

94
impl<'py> IntoPyObject<'py> for BinaryOp {
95
    type Target = PyAny;
96
    type Output = Bound<'py, Self::Target>;
97
    type Error = PyErr;
98

99
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
1,318✔
100
        imports::BINARY_OP.get_bound(py).call1((self as usize,))
1,318✔
101
    }
1,318✔
102
}
103

104
impl<'a, 'py> FromPyObject<'a, 'py> for BinaryOp {
105
    type Error = PyErr;
106

107
    fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
2,724✔
108
        let value = ob.getattr(intern!(ob.py(), "value"))?;
2,724✔
109
        Ok(bytemuck::checked::cast(value.extract::<u8>()?))
2,724✔
110
    }
2,724✔
111
}
112

113
/// A Python descriptor to prevent PyO3 from attempting to import the Python-side
114
/// enum before we're initialized.
115
#[pyclass(module = "qiskit._accelerate.circuit.classical.expr")]
116
struct PyBinaryOp;
117

118
#[pymethods]
119
impl PyBinaryOp {
120
    fn __get__(&self, obj: &Bound<PyAny>, _obj_type: &Bound<PyAny>) -> Py<PyAny> {
3,044✔
121
        imports::BINARY_OP.get_bound(obj.py()).clone().unbind()
3,044✔
122
    }
3,044✔
123
}
124

125
/// A binary expression.
126
///
127
/// Args:
128
///     op: The opcode describing which operation is being done.
129
///     left: The left-hand operand.
130
///     right: The right-hand operand.
131
///     type: The resolved type of the result.
132
#[pyclass(eq, extends = PyExpr, name = "Binary", module = "qiskit._accelerate.circuit.classical.expr")]
133
#[derive(PartialEq, Clone, Debug)]
134
pub struct PyBinary(Binary);
135

UNCOV
136
#[pymethods]
×
137
impl PyBinary {
138
    // The docstring for 'Op' is defined in Python (expr.py).
139
    #[classattr]
140
    #[allow(non_snake_case)]
141
    fn Op(py: Python) -> PyResult<Py<PyAny>> {
12✔
142
        PyBinaryOp.into_py_any(py)
12✔
143
    }
12✔
144

145
    #[new]
146
    #[pyo3(text_signature = "(op, left, right, type)")]
147
    fn new(py: Python, op: BinaryOp, left: Expr, right: Expr, ty: Type) -> PyResult<Py<Self>> {
2,724✔
148
        let constant = left.is_const() && right.is_const();
2,724✔
149
        Py::new(
2,724✔
150
            py,
2,724✔
151
            (
2,724✔
152
                PyBinary(Binary {
2,724✔
153
                    op,
2,724✔
154
                    left,
2,724✔
155
                    right,
2,724✔
156
                    ty,
2,724✔
157
                    constant,
2,724✔
158
                }),
2,724✔
159
                PyExpr(ExprKind::Binary),
2,724✔
160
            ),
2,724✔
161
        )
162
    }
2,724✔
163

164
    #[getter]
165
    fn get_op(&self, py: Python) -> PyResult<Py<PyAny>> {
1,318✔
166
        self.0.op.into_py_any(py)
1,318✔
167
    }
1,318✔
168

169
    #[getter]
170
    fn get_left(&self, py: Python) -> PyResult<Py<PyAny>> {
3,066✔
171
        self.0.left.clone().into_py_any(py)
3,066✔
172
    }
3,066✔
173

174
    #[getter]
175
    fn get_right(&self, py: Python) -> PyResult<Py<PyAny>> {
2,910✔
176
        self.0.right.clone().into_py_any(py)
2,910✔
177
    }
2,910✔
178

179
    #[getter]
180
    fn get_const(&self) -> bool {
414✔
181
        self.0.constant
414✔
182
    }
414✔
183

184
    #[getter]
185
    fn get_type(&self, py: Python) -> PyResult<Py<PyAny>> {
5,680✔
186
        self.0.ty.into_py_any(py)
5,680✔
187
    }
5,680✔
188

189
    fn accept<'py>(
2,638✔
190
        slf: PyRef<'py, Self>,
2,638✔
191
        visitor: &Bound<'py, PyAny>,
2,638✔
192
    ) -> PyResult<Bound<'py, PyAny>> {
2,638✔
193
        visitor.call_method1(intern!(visitor.py(), "visit_binary"), (slf,))
2,638✔
194
    }
2,638✔
195

196
    fn __reduce__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
310✔
197
        (
198
            py.get_type::<Self>(),
310✔
199
            (
200
                self.get_op(py)?,
310✔
201
                self.get_left(py)?,
310✔
202
                self.get_right(py)?,
310✔
203
                self.get_type(py)?,
310✔
204
            ),
205
        )
206
            .into_pyobject(py)
310✔
207
    }
310✔
208

209
    fn __repr__(&self, py: Python) -> PyResult<String> {
8✔
210
        Ok(format!(
8✔
211
            "Binary({}, {}, {}, {})",
8✔
212
            self.get_op(py)?.bind(py).repr()?,
8✔
213
            self.get_left(py)?.bind(py).repr()?,
8✔
214
            self.get_right(py)?.bind(py).repr()?,
8✔
215
            self.get_type(py)?.bind(py).repr()?,
8✔
216
        ))
217
    }
8✔
218
}
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