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

Qiskit / qiskit / 14114149916

27 Mar 2025 06:33PM UTC coverage: 88.061% (-0.008%) from 88.069%
14114149916

Pull #14068

github

web-flow
Merge 0ad84becf into ddb802506
Pull Request #14068: Port classical expressions (`Expr`) to Rust.

678 of 774 new or added lines in 16 files covered. (87.6%)

8 existing lines in 4 files now uncovered.

73173 of 83094 relevant lines covered (88.06%)

364373.16 hits per line

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

54.4
/crates/circuit/src/classical/expr/expr.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::{Binary, Cast, Index, Stretch, Unary, Value, Var};
14
use crate::classical::types::Type;
15
use pyo3::prelude::*;
16
use pyo3::{intern, IntoPyObjectExt};
17

18
#[derive(Clone, Debug, PartialEq)]
19
pub enum Expr {
20
    Unary(Box<Unary>),
21
    Binary(Box<Binary>),
22
    Cast(Box<Cast>),
23
    Value(Value),
24
    Var(Var),
25
    Stretch(Stretch),
26
    Index(Box<Index>),
27
}
28

29
impl Expr {
30
    pub fn is_const(&self) -> bool {
4,452✔
31
        match self {
4,452✔
32
            Expr::Unary(u) => u.constant,
38✔
33
            Expr::Binary(b) => b.constant,
666✔
34
            Expr::Cast(c) => c.constant,
106✔
35
            Expr::Value(_) => true,
1,780✔
36
            Expr::Var(_) => false,
1,794✔
37
            Expr::Stretch(_) => true,
62✔
38
            Expr::Index(i) => i.constant,
6✔
39
        }
40
    }
4,452✔
41

NEW
42
    pub fn ty(&self) -> Type {
×
NEW
43
        match self {
×
NEW
44
            Expr::Unary(u) => u.ty,
×
NEW
45
            Expr::Binary(b) => b.ty,
×
NEW
46
            Expr::Cast(c) => c.ty,
×
NEW
47
            Expr::Value(v) => match v {
×
NEW
48
                Value::Duration(_) => Type::Duration,
×
NEW
49
                Value::Float { ty, .. } => *ty,
×
NEW
50
                Value::Uint { ty, .. } => *ty,
×
51
            },
NEW
52
            Expr::Var(v) => match v {
×
NEW
53
                Var::Standalone { ty, .. } => *ty,
×
NEW
54
                Var::Bit { .. } => Type::Bool,
×
NEW
55
                Var::Register { ty, .. } => *ty,
×
56
            },
NEW
57
            Expr::Stretch(_) => Type::Duration,
×
NEW
58
            Expr::Index(i) => i.ty,
×
59
        }
NEW
60
    }
×
61

62
    pub fn vars(&self) -> impl Iterator<Item = &Var> {
8,264✔
63
        VarIterator(ExprIterator { stack: vec![self] })
8,264✔
64
    }
8,264✔
65
}
66

67
struct ExprIterator<'a> {
68
    stack: Vec<&'a Expr>,
69
}
70

71
impl<'a> Iterator for ExprIterator<'a> {
72
    type Item = &'a Expr;
73

74
    fn next(&mut self) -> Option<Self::Item> {
25,710✔
75
        let expr = self.stack.pop()?;
25,710✔
76
        match expr {
17,446✔
77
            Expr::Unary(u) => {
300✔
78
                self.stack.push(&u.operand);
300✔
79
            }
300✔
80
            Expr::Binary(b) => {
4,384✔
81
                self.stack.push(&b.left);
4,384✔
82
                self.stack.push(&b.right);
4,384✔
83
            }
4,384✔
84
            Expr::Cast(c) => self.stack.push(&c.operand),
82✔
85
            Expr::Value(_) => {}
6,230✔
86
            Expr::Var(_) => {}
6,434✔
NEW
87
            Expr::Stretch(_) => {}
×
88
            Expr::Index(i) => {
16✔
89
                self.stack.push(&i.index);
16✔
90
                self.stack.push(&i.target);
16✔
91
            }
16✔
92
        }
93
        Some(expr)
17,446✔
94
    }
25,710✔
95
}
96

97
struct VarIterator<'a>(ExprIterator<'a>);
98

99
impl<'a> Iterator for VarIterator<'a> {
100
    type Item = &'a Var;
101

102
    fn next(&mut self) -> Option<Self::Item> {
14,698✔
103
        for expr in self.0.by_ref() {
17,446✔
104
            if let Expr::Var(v) = expr {
17,446✔
105
                return Some(v);
6,434✔
106
            }
11,012✔
107
        }
108
        None
8,264✔
109
    }
14,698✔
110
}
111

112
impl From<Unary> for Expr {
NEW
113
    fn from(value: Unary) -> Self {
×
NEW
114
        Expr::Unary(Box::new(value))
×
NEW
115
    }
×
116
}
117

118
impl From<Box<Unary>> for Expr {
NEW
119
    fn from(value: Box<Unary>) -> Self {
×
NEW
120
        Expr::Unary(value)
×
NEW
121
    }
×
122
}
123

124
impl From<Binary> for Expr {
NEW
125
    fn from(value: Binary) -> Self {
×
NEW
126
        Expr::Binary(Box::new(value))
×
NEW
127
    }
×
128
}
129

130
impl From<Box<Binary>> for Expr {
NEW
131
    fn from(value: Box<Binary>) -> Self {
×
NEW
132
        Expr::Binary(value)
×
NEW
133
    }
×
134
}
135

136
impl From<Cast> for Expr {
NEW
137
    fn from(value: Cast) -> Self {
×
NEW
138
        Expr::Cast(Box::new(value))
×
NEW
139
    }
×
140
}
141

142
impl From<Box<Cast>> for Expr {
NEW
143
    fn from(value: Box<Cast>) -> Self {
×
NEW
144
        Expr::Cast(value)
×
NEW
145
    }
×
146
}
147

148
impl From<Value> for Expr {
NEW
149
    fn from(value: Value) -> Self {
×
NEW
150
        Expr::Value(value)
×
NEW
151
    }
×
152
}
153

154
impl From<Var> for Expr {
NEW
155
    fn from(value: Var) -> Self {
×
NEW
156
        Expr::Var(value)
×
NEW
157
    }
×
158
}
159

160
impl From<Stretch> for Expr {
NEW
161
    fn from(value: Stretch) -> Self {
×
NEW
162
        Expr::Stretch(value)
×
NEW
163
    }
×
164
}
165

166
impl From<Index> for Expr {
NEW
167
    fn from(value: Index) -> Self {
×
NEW
168
        Expr::Index(Box::new(value))
×
NEW
169
    }
×
170
}
171

172
impl From<Box<Index>> for Expr {
NEW
173
    fn from(value: Box<Index>) -> Self {
×
NEW
174
        Expr::Index(value)
×
NEW
175
    }
×
176
}
177

178
/// Root base class of all nodes in the expression tree.  The base case should never be
179
/// instantiated directly.
180
///
181
/// This must not be subclassed by users; subclasses form the internal data of the representation of
182
/// expressions, and it does not make sense to add more outside of Qiskit library code.
183
///
184
/// All subclasses are responsible for setting their ``type`` attribute in their ``__init__``, and
185
/// should not call the parent initializer."""
186
#[pyclass(
12✔
187
    eq,
12✔
188
    hash,
12✔
189
    subclass,
12✔
190
    frozen,
12✔
191
    name = "Expr",
12✔
192
    module = "qiskit._accelerate.circuit"
12✔
193
)]
12✔
194
#[derive(PartialEq, Clone, Copy, Debug, Hash)]
195
pub struct PyExpr(pub ExprKind); // ExprKind is used for fast extraction from Python
196

NEW
197
#[pymethods]
×
198
impl PyExpr {
199
    /// Call the relevant ``visit_*`` method on the given :class:`ExprVisitor`.  The usual entry
200
    /// point for a simple visitor is to construct it, and then call :meth:`accept` on the root
201
    /// object to be visited.  For example::
202
    ///
203
    ///     expr = ...
204
    ///     visitor = MyVisitor()
205
    ///     visitor.accept(expr)
206
    ///
207
    /// Subclasses of :class:`Expr` should override this to call the correct virtual method on the
208
    /// visitor.  This implements double dispatch with the visitor."""
209
    /// return visitor.visit_generic(self)
NEW
210
    fn accept<'py>(
×
NEW
211
        slf: PyRef<'py, Self>,
×
NEW
212
        visitor: &Bound<'py, PyAny>,
×
NEW
213
    ) -> PyResult<Bound<'py, PyAny>> {
×
NEW
214
        visitor.call_method1(intern!(visitor.py(), "visit_generic"), (slf,))
×
NEW
215
    }
×
216
}
217

218
#[repr(u8)]
219
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
220
pub enum ExprKind {
221
    Unary,
222
    Binary,
223
    Value,
224
    Var,
225
    Cast,
226
    Stretch,
227
    Index,
228
}
229

230
impl<'py> IntoPyObject<'py> for Expr {
231
    type Target = PyAny;
232
    type Output = Bound<'py, PyAny>;
233
    type Error = PyErr;
234

235
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
7,050✔
236
        match self {
7,050✔
237
            Expr::Unary(u) => u.into_bound_py_any(py),
142✔
238
            Expr::Binary(b) => b.into_bound_py_any(py),
1,764✔
239
            Expr::Cast(c) => c.into_bound_py_any(py),
48✔
240
            Expr::Value(v) => v.into_bound_py_any(py),
2,538✔
241
            Expr::Var(v) => v.into_bound_py_any(py),
2,440✔
242
            Expr::Stretch(s) => s.into_bound_py_any(py),
98✔
243
            Expr::Index(i) => i.into_bound_py_any(py),
20✔
244
        }
245
    }
7,050✔
246
}
247

248
impl<'py> FromPyObject<'py> for Expr {
249
    fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
31,438✔
250
        let expr: PyRef<'_, PyExpr> = ob.downcast()?.borrow();
31,438✔
251
        match expr.0 {
14,210✔
252
            ExprKind::Unary => Ok(Expr::Unary(Box::new(ob.extract()?))),
322✔
253
            ExprKind::Binary => Ok(Expr::Binary(Box::new(ob.extract()?))),
2,724✔
254
            ExprKind::Value => Ok(Expr::Value(ob.extract()?)),
4,584✔
255
            ExprKind::Var => Ok(Expr::Var(ob.extract()?)),
6,344✔
256
            ExprKind::Cast => Ok(Expr::Cast(Box::new(ob.extract()?))),
154✔
257
            ExprKind::Stretch => Ok(Expr::Stretch(ob.extract()?)),
62✔
258
            ExprKind::Index => Ok(Expr::Index(Box::new(ob.extract()?))),
20✔
259
        }
260
    }
31,438✔
261
}
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