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

Qiskit / qiskit / 15999970202

01 Jul 2025 12:56PM UTC coverage: 87.736% (+0.05%) from 87.69%
15999970202

push

github

web-flow
Fix future clippy issues (#14461)

* Fix future clippy issues

This commit fixes some issues the clippy on nightly has identified as
issues in the code. These will all become CI failures at some future
date (after we raise the MSRV to match the current rules nightly is
adding). However, most of the changes are good improvements and in this
case most are code style issues that make things a bit more concise. The
bulk of the changes here are just moving to use captured identifiers in
format strings.

* Update new qasm3 code too

* Fix new issues introduced in new commits

* Remove unneeded return statements

* Make lifetime syntaxes consistent

A new nightly rule was added to ensure that lifetime syntaxes are
matched in function signatures. In many cases we were excluding the
elided anonymous lifetime parameter being returned in the source which
clippy asserts is potentially confusing. This commit adds the explicit
anonymous paramter to these places to fix the warning.

* Fix rust tests too

129 of 244 new or added lines in 46 files covered. (52.87%)

4 existing lines in 2 files now uncovered.

80982 of 92302 relevant lines covered (87.74%)

506230.69 hits per line

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

91.45
/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
/// A classical expression.
19
///
20
/// Variants that themselves contain [Expr]s are boxed. This is done instead
21
/// of boxing the contained [Expr]s within the specific type to reduce the
22
/// number of boxes we need (e.g. Binary would otherwise contain two boxed
23
/// expressions).
24
#[derive(Clone, Debug, PartialEq)]
25
pub enum Expr {
26
    Unary(Box<Unary>),
27
    Binary(Box<Binary>),
28
    Cast(Box<Cast>),
29
    Value(Value),
30
    Var(Var),
31
    Stretch(Stretch),
32
    Index(Box<Index>),
33
}
34

35
#[derive(Debug, PartialEq)]
36
pub enum ExprRef<'a> {
37
    Unary(&'a Unary),
38
    Binary(&'a Binary),
39
    Cast(&'a Cast),
40
    Value(&'a Value),
41
    Var(&'a Var),
42
    Stretch(&'a Stretch),
43
    Index(&'a Index),
44
}
45

46
#[derive(Debug, PartialEq)]
47
pub enum ExprRefMut<'a> {
48
    Unary(&'a mut Unary),
49
    Binary(&'a mut Binary),
50
    Cast(&'a mut Cast),
51
    Value(&'a mut Value),
52
    Var(&'a mut Var),
53
    Stretch(&'a mut Stretch),
54
    Index(&'a mut Index),
55
}
56

57
#[derive(Clone, Debug, PartialEq)]
58
pub enum IdentifierRef<'a> {
59
    Var(&'a Var),
60
    Stretch(&'a Stretch),
61
}
62

63
impl Expr {
64
    /// Converts from `&Expr` to `ExprRef`.
65
    pub fn as_ref(&self) -> ExprRef<'_> {
10,006✔
66
        match self {
10,006✔
67
            Expr::Unary(u) => ExprRef::Unary(u.as_ref()),
184✔
68
            Expr::Binary(b) => ExprRef::Binary(b.as_ref()),
1,474✔
69
            Expr::Cast(c) => ExprRef::Cast(c.as_ref()),
84✔
70
            Expr::Value(v) => ExprRef::Value(v),
2,765✔
71
            Expr::Var(v) => ExprRef::Var(v),
5,475✔
72
            Expr::Stretch(s) => ExprRef::Stretch(s),
6✔
73
            Expr::Index(i) => ExprRef::Index(i.as_ref()),
18✔
74
        }
75
    }
10,006✔
76

77
    /// Converts from `&mut Expr` to `ExprRefMut`.
78
    pub fn as_mut(&mut self) -> ExprRefMut<'_> {
69✔
79
        match self {
69✔
80
            Expr::Unary(u) => ExprRefMut::Unary(u.as_mut()),
7✔
81
            Expr::Binary(b) => ExprRefMut::Binary(b.as_mut()),
16✔
82
            Expr::Cast(c) => ExprRefMut::Cast(c.as_mut()),
4✔
83
            Expr::Value(v) => ExprRefMut::Value(v),
10✔
84
            Expr::Var(v) => ExprRefMut::Var(v),
30✔
85
            Expr::Stretch(s) => ExprRefMut::Stretch(s),
2✔
86
            Expr::Index(i) => ExprRefMut::Index(i.as_mut()),
×
87
        }
88
    }
69✔
89

90
    /// The const-ness of the expression.
91
    pub fn is_const(&self) -> bool {
3,696✔
92
        match self {
3,696✔
93
            Expr::Unary(u) => u.constant,
38✔
94
            Expr::Binary(b) => b.constant,
374✔
95
            Expr::Cast(c) => c.constant,
104✔
96
            Expr::Value(_) => true,
1,412✔
97
            Expr::Var(_) => false,
1,684✔
98
            Expr::Stretch(_) => true,
78✔
99
            Expr::Index(i) => i.constant,
6✔
100
        }
101
    }
3,696✔
102

103
    /// The expression's [Type].
104
    pub fn ty(&self) -> Type {
×
105
        match self {
×
106
            Expr::Unary(u) => u.ty,
×
107
            Expr::Binary(b) => b.ty,
×
108
            Expr::Cast(c) => c.ty,
×
109
            Expr::Value(v) => match v {
×
110
                Value::Duration(_) => Type::Duration,
×
111
                Value::Float { ty, .. } => *ty,
×
112
                Value::Uint { ty, .. } => *ty,
×
113
            },
114
            Expr::Var(v) => match v {
×
115
                Var::Standalone { ty, .. } => *ty,
×
116
                Var::Bit { .. } => Type::Bool,
×
117
                Var::Register { ty, .. } => *ty,
×
118
            },
119
            Expr::Stretch(_) => Type::Duration,
×
120
            Expr::Index(i) => i.ty,
×
121
        }
122
    }
×
123

124
    /// Returns an iterator over the identifier nodes in this expression in some
125
    /// deterministic order.
126
    pub fn identifiers(&self) -> impl Iterator<Item = IdentifierRef<'_>> {
1✔
127
        IdentIterator(ExprIterator { stack: vec![self] })
1✔
128
    }
1✔
129

130
    /// Returns an iterator over the [Var] nodes in this expression in some
131
    /// deterministic order.
132
    pub fn vars(&self) -> impl Iterator<Item = &Var> {
6,730✔
133
        VarIterator(ExprIterator { stack: vec![self] })
6,730✔
134
    }
6,730✔
135

136
    /// Returns an iterator over all nodes in this expression in some deterministic
137
    /// order.
138
    pub fn iter(&self) -> impl Iterator<Item = ExprRef<'_>> {
93✔
139
        ExprIterator { stack: vec![self] }
93✔
140
    }
93✔
141

142
    /// Visits all nodes by mutable reference, in a post-order traversal.
143
    pub fn visit_mut<F>(&mut self, mut visitor: F) -> PyResult<()>
26✔
144
    where
26✔
145
        F: FnMut(ExprRefMut) -> PyResult<()>,
26✔
146
    {
147
        self.visit_mut_impl(&mut visitor)
26✔
148
    }
26✔
149

150
    fn visit_mut_impl<F>(&mut self, visitor: &mut F) -> PyResult<()>
69✔
151
    where
69✔
152
        F: FnMut(ExprRefMut) -> PyResult<()>,
69✔
153
    {
154
        match self {
69✔
155
            Expr::Unary(u) => u.operand.visit_mut_impl(visitor)?,
7✔
156
            Expr::Binary(b) => {
16✔
157
                b.left.visit_mut_impl(visitor)?;
16✔
158
                b.right.visit_mut_impl(visitor)?;
16✔
159
            }
160
            Expr::Cast(c) => c.operand.visit_mut_impl(visitor)?,
4✔
161
            Expr::Value(_) => {}
10✔
162
            Expr::Var(_) => {}
30✔
163
            Expr::Stretch(_) => {}
2✔
164
            Expr::Index(i) => {
×
165
                i.target.visit_mut_impl(visitor)?;
×
166
                i.index.visit_mut_impl(visitor)?;
×
167
            }
168
        }
169
        visitor(self.as_mut())
69✔
170
    }
69✔
171

172
    /// Do these two expressions have exactly the same tree structure?
173
    pub fn structurally_equivalent(&self, other: &Expr) -> bool {
45✔
174
        let identity_key = |v: &Var| v.clone();
45✔
175
        self.structurally_equivalent_by_key(identity_key, other, identity_key)
45✔
176
    }
45✔
177

178
    /// Do these two expressions have exactly the same tree structure, up to some key function for
179
    /// [Var] nodes?
180
    ///
181
    /// In other words, are these two expressions the exact same trees, except we compare the
182
    /// [Var] nodes by calling the appropriate `*_var_key` function on them, and comparing
183
    /// that output for equality.  This function does not allow any semantic "equivalences" such as
184
    /// asserting that `a == b` is equivalent to `b == a`; the evaluation order of the operands
185
    /// could, in general, cause such a statement to be false.
186
    pub fn structurally_equivalent_by_key<F1, F2, K>(
46✔
187
        &self,
46✔
188
        mut self_var_key: F1,
46✔
189
        other: &Expr,
46✔
190
        mut other_var_key: F2,
46✔
191
    ) -> bool
46✔
192
    where
46✔
193
        F1: FnMut(&Var) -> K,
46✔
194
        F2: FnMut(&Var) -> K,
46✔
195
        K: PartialEq,
46✔
196
    {
197
        let mut self_nodes = self.iter();
46✔
198
        let mut other_nodes = other.iter();
46✔
199
        loop {
200
            match (self_nodes.next(), other_nodes.next()) {
114✔
201
                (Some(a), Some(b)) => match (a, b) {
103✔
202
                    (ExprRef::Unary(a), ExprRef::Unary(b)) => {
3✔
203
                        if a.op != b.op || a.ty != b.ty || a.constant != b.constant {
3✔
204
                            return false;
×
205
                        }
3✔
206
                    }
207
                    (ExprRef::Binary(a), ExprRef::Binary(b)) => {
43✔
208
                        if a.op != b.op || a.ty != b.ty || a.constant != b.constant {
43✔
209
                            return false;
×
210
                        }
43✔
211
                    }
212
                    (ExprRef::Cast(a), ExprRef::Cast(b)) => {
1✔
213
                        if a.ty != b.ty || a.constant != b.constant {
1✔
214
                            return false;
×
215
                        }
1✔
216
                    }
217
                    (ExprRef::Value(a), ExprRef::Value(b)) => {
9✔
218
                        if a != b {
9✔
219
                            return false;
×
220
                        }
9✔
221
                    }
222
                    (ExprRef::Var(a), ExprRef::Var(b)) => {
11✔
223
                        if a.ty() != b.ty() {
11✔
224
                            return false;
×
225
                        }
11✔
226
                        if self_var_key(a) != other_var_key(b) {
11✔
227
                            return false;
1✔
228
                        }
10✔
229
                    }
230
                    (ExprRef::Stretch(a), ExprRef::Stretch(b)) => {
1✔
231
                        if a.uuid != b.uuid {
1✔
232
                            return false;
×
233
                        }
1✔
234
                    }
235
                    (ExprRef::Index(a), ExprRef::Index(b)) => {
1✔
236
                        if a.ty != b.ty || a.constant != b.constant {
1✔
237
                            return false;
×
238
                        }
1✔
239
                    }
240
                    _ => return false,
34✔
241
                },
242
                (None, None) => return true,
11✔
243
                _ => return false,
×
244
            }
245
        }
246
    }
46✔
247
}
248

249
/// A private iterator over the [Expr] nodes of an expression
250
/// by reference.
251
///
252
/// The first node reference returned is the [Expr] itself.
253
struct ExprIterator<'a> {
254
    stack: Vec<&'a Expr>,
255
}
256

257
impl<'a> Iterator for ExprIterator<'a> {
258
    type Item = ExprRef<'a>;
259

260
    fn next(&mut self) -> Option<Self::Item> {
16,759✔
261
        let expr = self.stack.pop()?;
16,759✔
262
        match expr {
10,006✔
263
            Expr::Unary(u) => {
184✔
264
                self.stack.push(&u.operand);
184✔
265
            }
184✔
266
            Expr::Binary(b) => {
1,474✔
267
                self.stack.push(&b.left);
1,474✔
268
                self.stack.push(&b.right);
1,474✔
269
            }
1,474✔
270
            Expr::Cast(c) => self.stack.push(&c.operand),
84✔
271
            Expr::Value(_) => {}
2,765✔
272
            Expr::Var(_) => {}
5,475✔
273
            Expr::Stretch(_) => {}
6✔
274
            Expr::Index(i) => {
18✔
275
                self.stack.push(&i.index);
18✔
276
                self.stack.push(&i.target);
18✔
277
            }
18✔
278
        }
279
        Some(expr.as_ref())
10,006✔
280
    }
16,759✔
281
}
282

283
/// A private iterator over the [Var] nodes contained within an [Expr].
284
struct VarIterator<'a>(ExprIterator<'a>);
285

286
impl<'a> Iterator for VarIterator<'a> {
287
    type Item = &'a Var;
288

289
    fn next(&mut self) -> Option<Self::Item> {
12,146✔
290
        for expr in self.0.by_ref() {
12,150✔
291
            if let ExprRef::Var(v) = expr {
9,786✔
292
                return Some(v);
5,417✔
293
            }
4,369✔
294
        }
295
        None
6,729✔
296
    }
12,146✔
297
}
298

299
/// A private iterator over the [Var] and [Stretch] nodes contained within an [Expr].
300
struct IdentIterator<'a>(ExprIterator<'a>);
301

302
impl<'a> Iterator for IdentIterator<'a> {
303
    type Item = IdentifierRef<'a>;
304

305
    fn next(&mut self) -> Option<Self::Item> {
4✔
306
        for expr in self.0.by_ref() {
7✔
307
            if let ExprRef::Var(v) = expr {
7✔
308
                return Some(IdentifierRef::Var(v));
1✔
309
            }
6✔
310
            if let ExprRef::Stretch(s) = expr {
6✔
311
                return Some(IdentifierRef::Stretch(s));
2✔
312
            }
4✔
313
        }
314
        None
1✔
315
    }
4✔
316
}
317

318
impl From<Unary> for Expr {
319
    fn from(value: Unary) -> Self {
2✔
320
        Expr::Unary(Box::new(value))
2✔
321
    }
2✔
322
}
323

324
impl From<Box<Unary>> for Expr {
325
    fn from(value: Box<Unary>) -> Self {
×
326
        Expr::Unary(value)
×
327
    }
×
328
}
329

330
impl From<Binary> for Expr {
331
    fn from(value: Binary) -> Self {
11✔
332
        Expr::Binary(Box::new(value))
11✔
333
    }
11✔
334
}
335

336
impl From<Box<Binary>> for Expr {
337
    fn from(value: Box<Binary>) -> Self {
×
338
        Expr::Binary(value)
×
339
    }
×
340
}
341

342
impl From<Cast> for Expr {
343
    fn from(value: Cast) -> Self {
×
344
        Expr::Cast(Box::new(value))
×
345
    }
×
346
}
347

348
impl From<Box<Cast>> for Expr {
349
    fn from(value: Box<Cast>) -> Self {
×
350
        Expr::Cast(value)
×
351
    }
×
352
}
353

354
impl From<Value> for Expr {
355
    fn from(value: Value) -> Self {
4✔
356
        Expr::Value(value)
4✔
357
    }
4✔
358
}
359

360
impl From<Var> for Expr {
361
    fn from(value: Var) -> Self {
6✔
362
        Expr::Var(value)
6✔
363
    }
6✔
364
}
365

366
impl From<Stretch> for Expr {
367
    fn from(value: Stretch) -> Self {
6✔
368
        Expr::Stretch(value)
6✔
369
    }
6✔
370
}
371

372
impl From<Index> for Expr {
373
    fn from(value: Index) -> Self {
×
374
        Expr::Index(Box::new(value))
×
375
    }
×
376
}
377

378
impl From<Box<Index>> for Expr {
379
    fn from(value: Box<Index>) -> Self {
×
380
        Expr::Index(value)
×
381
    }
×
382
}
383

384
/// Root base class of all nodes in the expression tree.  The base case should never be
385
/// instantiated directly.
386
///
387
/// This must not be subclassed by users; subclasses form the internal data of the representation of
388
/// expressions, and it does not make sense to add more outside of Qiskit library code.
389
///
390
/// All subclasses are responsible for setting their ``type`` attribute in their ``__init__``, and
391
/// should not call the parent initializer."""
392
#[pyclass(
×
393
    eq,
×
394
    hash,
×
395
    subclass,
396
    frozen,
397
    name = "Expr",
398
    module = "qiskit._accelerate.circuit.classical.expr"
399
)]
×
400
#[derive(PartialEq, Clone, Copy, Debug, Hash)]
401
pub struct PyExpr(pub ExprKind); // ExprKind is used for fast extraction from Python
402

403
#[pymethods]
404
impl PyExpr {
405
    /// Call the relevant ``visit_*`` method on the given :class:`ExprVisitor`.  The usual entry
406
    /// point for a simple visitor is to construct it, and then call :meth:`accept` on the root
407
    /// object to be visited.  For example::
408
    ///
409
    ///     expr = ...
410
    ///     visitor = MyVisitor()
411
    ///     visitor.accept(expr)
412
    ///
413
    /// Subclasses of :class:`Expr` should override this to call the correct virtual method on the
414
    /// visitor.  This implements double dispatch with the visitor."""
415
    /// return visitor.visit_generic(self)
416
    fn accept<'py>(
×
417
        slf: PyRef<'py, Self>,
×
418
        visitor: &Bound<'py, PyAny>,
×
419
    ) -> PyResult<Bound<'py, PyAny>> {
×
420
        visitor.call_method1(intern!(visitor.py(), "visit_generic"), (slf,))
×
421
    }
×
422
}
423

424
/// The expression's kind, used internally during Python instance extraction to avoid
425
/// `isinstance` checks.
426
#[repr(u8)]
427
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
428
pub enum ExprKind {
429
    Unary,
430
    Binary,
431
    Value,
432
    Var,
433
    Cast,
434
    Stretch,
435
    Index,
436
}
437

438
impl<'py> IntoPyObject<'py> for Expr {
439
    type Target = PyAny;
440
    type Output = Bound<'py, PyAny>;
441
    type Error = PyErr;
442

443
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
5,072✔
444
        match self {
5,072✔
445
            Expr::Unary(u) => u.into_bound_py_any(py),
148✔
446
            Expr::Binary(b) => b.into_bound_py_any(py),
1,046✔
447
            Expr::Cast(c) => c.into_bound_py_any(py),
48✔
448
            Expr::Value(v) => v.into_bound_py_any(py),
1,454✔
449
            Expr::Var(v) => v.into_bound_py_any(py),
2,246✔
450
            Expr::Stretch(s) => s.into_bound_py_any(py),
110✔
451
            Expr::Index(i) => i.into_bound_py_any(py),
20✔
452
        }
453
    }
5,072✔
454
}
455

456
impl<'py> FromPyObject<'py> for Expr {
457
    fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
29,256✔
458
        let expr: PyRef<'_, PyExpr> = ob.downcast()?.borrow();
29,256✔
459
        match expr.0 {
11,852✔
460
            ExprKind::Unary => Ok(Expr::Unary(Box::new(ob.extract()?))),
204✔
461
            ExprKind::Binary => Ok(Expr::Binary(Box::new(ob.extract()?))),
1,664✔
462
            ExprKind::Value => Ok(Expr::Value(ob.extract()?)),
3,972✔
463
            ExprKind::Var => Ok(Expr::Var(ob.extract()?)),
5,760✔
464
            ExprKind::Cast => Ok(Expr::Cast(Box::new(ob.extract()?))),
154✔
465
            ExprKind::Stretch => Ok(Expr::Stretch(ob.extract()?)),
78✔
466
            ExprKind::Index => Ok(Expr::Index(Box::new(ob.extract()?))),
20✔
467
        }
468
    }
29,256✔
469
}
470

471
#[cfg(test)]
472
mod tests {
473
    use crate::bit::{ClassicalRegister, ShareableClbit};
474
    use crate::classical::expr::{
475
        Binary, BinaryOp, Cast, Expr, ExprRef, ExprRefMut, IdentifierRef, Index, Stretch, Unary,
476
        UnaryOp, Value, Var,
477
    };
478
    use crate::classical::types::Type;
479
    use crate::duration::Duration;
480
    use pyo3::PyResult;
481
    use uuid::Uuid;
482

483
    #[test]
484
    fn test_vars() {
1✔
485
        let expr: Expr = Binary {
1✔
486
            op: BinaryOp::BitAnd,
1✔
487
            left: Unary {
1✔
488
                op: UnaryOp::BitNot,
1✔
489
                operand: Var::Standalone {
1✔
490
                    uuid: Uuid::new_v4().as_u128(),
1✔
491
                    name: "test".to_string(),
1✔
492
                    ty: Type::Bool,
1✔
493
                }
1✔
494
                .into(),
1✔
495
                ty: Type::Bool,
1✔
496
                constant: false,
1✔
497
            }
1✔
498
            .into(),
1✔
499
            right: Var::Bit {
1✔
500
                bit: ShareableClbit::new_anonymous(),
1✔
501
            }
1✔
502
            .into(),
1✔
503
            ty: Type::Bool,
1✔
504
            constant: false,
1✔
505
        }
1✔
506
        .into();
1✔
507

508
        let vars: Vec<&Var> = expr.vars().collect();
1✔
509
        assert!(matches!(
1✔
510
            vars.as_slice(),
1✔
511
            [Var::Bit { .. }, Var::Standalone { .. }]
1✔
512
        ));
513
    }
1✔
514

515
    #[test]
516
    fn test_identifiers() {
1✔
517
        let expr: Expr = Binary {
1✔
518
            op: BinaryOp::Mul,
1✔
519
            left: Binary {
1✔
520
                op: BinaryOp::Add,
1✔
521
                left: Var::Standalone {
1✔
522
                    uuid: Uuid::new_v4().as_u128(),
1✔
523
                    name: "test".to_string(),
1✔
524
                    ty: Type::Duration,
1✔
525
                }
1✔
526
                .into(),
1✔
527
                right: Stretch {
1✔
528
                    uuid: Uuid::new_v4().as_u128(),
1✔
529
                    name: "test".to_string(),
1✔
530
                }
1✔
531
                .into(),
1✔
532
                ty: Type::Duration,
1✔
533
                constant: false,
1✔
534
            }
1✔
535
            .into(),
1✔
536
            right: Binary {
1✔
537
                op: BinaryOp::Div,
1✔
538
                left: Stretch {
1✔
539
                    uuid: Uuid::new_v4().as_u128(),
1✔
540
                    name: "test".to_string(),
1✔
541
                }
1✔
542
                .into(),
1✔
543
                right: Value::Duration(Duration::dt(1000)).into(),
1✔
544
                ty: Type::Float,
1✔
545
                constant: true,
1✔
546
            }
1✔
547
            .into(),
1✔
548
            ty: Type::Bool,
1✔
549
            constant: false,
1✔
550
        }
1✔
551
        .into();
1✔
552

553
        let identifiers: Vec<IdentifierRef> = expr.identifiers().collect();
1✔
554
        assert!(matches!(
1✔
555
            identifiers.as_slice(),
1✔
556
            [
1✔
557
                IdentifierRef::Stretch(Stretch { .. }),
1✔
558
                IdentifierRef::Stretch(Stretch { .. }),
1✔
559
                IdentifierRef::Var(Var::Standalone { .. }),
1✔
560
            ]
1✔
561
        ));
562
    }
1✔
563

564
    #[test]
565
    fn test_iter() {
1✔
566
        let expr: Expr = Binary {
1✔
567
            op: BinaryOp::Mul,
1✔
568
            left: Binary {
1✔
569
                op: BinaryOp::Add,
1✔
570
                left: Var::Standalone {
1✔
571
                    uuid: Uuid::new_v4().as_u128(),
1✔
572
                    name: "test".to_string(),
1✔
573
                    ty: Type::Duration,
1✔
574
                }
1✔
575
                .into(),
1✔
576
                right: Stretch {
1✔
577
                    uuid: Uuid::new_v4().as_u128(),
1✔
578
                    name: "test".to_string(),
1✔
579
                }
1✔
580
                .into(),
1✔
581
                ty: Type::Duration,
1✔
582
                constant: false,
1✔
583
            }
1✔
584
            .into(),
1✔
585
            right: Binary {
1✔
586
                op: BinaryOp::Div,
1✔
587
                left: Stretch {
1✔
588
                    uuid: Uuid::new_v4().as_u128(),
1✔
589
                    name: "test".to_string(),
1✔
590
                }
1✔
591
                .into(),
1✔
592
                right: Value::Duration(Duration::dt(1000)).into(),
1✔
593
                ty: Type::Float,
1✔
594
                constant: true,
1✔
595
            }
1✔
596
            .into(),
1✔
597
            ty: Type::Bool,
1✔
598
            constant: false,
1✔
599
        }
1✔
600
        .into();
1✔
601

602
        let exprs: Vec<ExprRef> = expr.iter().collect();
1✔
603
        assert!(matches!(
1✔
604
            exprs.as_slice(),
1✔
605
            [
1✔
606
                ExprRef::Binary(Binary {
1✔
607
                    op: BinaryOp::Mul,
1✔
608
                    ..
1✔
609
                }),
1✔
610
                ExprRef::Binary(Binary {
1✔
611
                    op: BinaryOp::Div,
1✔
612
                    ..
1✔
613
                }),
1✔
614
                ExprRef::Value(Value::Duration(..)),
1✔
615
                ExprRef::Stretch(Stretch { .. }),
1✔
616
                ExprRef::Binary(Binary {
1✔
617
                    op: BinaryOp::Add,
1✔
618
                    ..
1✔
619
                }),
1✔
620
                ExprRef::Stretch(Stretch { .. }),
1✔
621
                ExprRef::Var(Var::Standalone { .. }),
1✔
622
            ]
1✔
623
        ));
624
    }
1✔
625

626
    #[test]
627
    fn test_visit_mut_ordering() -> PyResult<()> {
1✔
628
        let mut expr: Expr = Binary {
1✔
629
            op: BinaryOp::Mul,
1✔
630
            left: Binary {
1✔
631
                op: BinaryOp::Add,
1✔
632
                left: Var::Standalone {
1✔
633
                    uuid: Uuid::new_v4().as_u128(),
1✔
634
                    name: "test".to_string(),
1✔
635
                    ty: Type::Duration,
1✔
636
                }
1✔
637
                .into(),
1✔
638
                right: Stretch {
1✔
639
                    uuid: Uuid::new_v4().as_u128(),
1✔
640
                    name: "test".to_string(),
1✔
641
                }
1✔
642
                .into(),
1✔
643
                ty: Type::Duration,
1✔
644
                constant: false,
1✔
645
            }
1✔
646
            .into(),
1✔
647
            right: Binary {
1✔
648
                op: BinaryOp::Div,
1✔
649
                left: Stretch {
1✔
650
                    uuid: Uuid::new_v4().as_u128(),
1✔
651
                    name: "test".to_string(),
1✔
652
                }
1✔
653
                .into(),
1✔
654
                right: Value::Duration(Duration::dt(1000)).into(),
1✔
655
                ty: Type::Float,
1✔
656
                constant: true,
1✔
657
            }
1✔
658
            .into(),
1✔
659
            ty: Type::Bool,
1✔
660
            constant: false,
1✔
661
        }
1✔
662
        .into();
1✔
663

664
        // These get *consumed* by every visit, so by the end we expect this
665
        // iterator to be empty. The ordering here is post-order, LRN.
666
        let mut order = [
1✔
667
            |x: &ExprRefMut| matches!(x, ExprRefMut::Var(Var::Standalone { .. })),
1✔
668
            |x: &ExprRefMut| matches!(x, ExprRefMut::Stretch(Stretch { .. })),
1✔
669
            |x: &ExprRefMut| {
1✔
670
                matches!(
×
671
                    x,
1✔
672
                    ExprRefMut::Binary(Binary {
673
                        op: BinaryOp::Add,
674
                        ..
675
                    })
676
                )
677
            },
1✔
678
            |x: &ExprRefMut| matches!(x, ExprRefMut::Stretch(Stretch { .. })),
1✔
679
            |x: &ExprRefMut| matches!(x, ExprRefMut::Value(Value::Duration(..))),
1✔
680
            |x: &ExprRefMut| {
1✔
681
                matches!(
×
682
                    x,
1✔
683
                    ExprRefMut::Binary(Binary {
684
                        op: BinaryOp::Div,
685
                        ..
686
                    })
687
                )
688
            },
1✔
689
            |x: &ExprRefMut| {
1✔
690
                matches!(
×
691
                    x,
1✔
692
                    ExprRefMut::Binary(Binary {
693
                        op: BinaryOp::Mul,
694
                        ..
695
                    })
696
                )
697
            },
1✔
698
        ]
699
        .into_iter();
1✔
700

701
        expr.visit_mut(|x| {
7✔
702
            assert!(order.next().unwrap()(&x));
7✔
703
            Ok(())
7✔
704
        })?;
7✔
705

706
        assert!(order.next().is_none());
1✔
707
        Ok(())
1✔
708
    }
1✔
709

710
    #[test]
711
    fn test_visit_mut() -> PyResult<()> {
1✔
712
        let mut expr: Expr = Binary {
1✔
713
            op: BinaryOp::BitAnd,
1✔
714
            left: Unary {
1✔
715
                op: UnaryOp::BitNot,
1✔
716
                operand: Var::Standalone {
1✔
717
                    uuid: Uuid::new_v4().as_u128(),
1✔
718
                    name: "test".to_string(),
1✔
719
                    ty: Type::Bool,
1✔
720
                }
1✔
721
                .into(),
1✔
722
                ty: Type::Bool,
1✔
723
                constant: false,
1✔
724
            }
1✔
725
            .into(),
1✔
726
            right: Value::Uint {
1✔
727
                raw: 1,
1✔
728
                ty: Type::Bool,
1✔
729
            }
1✔
730
            .into(),
1✔
731
            ty: Type::Bool,
1✔
732
            constant: false,
1✔
733
        }
1✔
734
        .into();
1✔
735

736
        expr.visit_mut(|x| match x {
1✔
737
            ExprRefMut::Var(Var::Standalone { name, .. }) => {
1✔
738
                *name = "updated".to_string();
1✔
739
                Ok(())
1✔
740
            }
741
            _ => Ok(()),
3✔
742
        })?;
4✔
743

744
        let Var::Standalone { name, .. } = expr.vars().next().unwrap() else {
1✔
745
            panic!("wrong var type")
×
746
        };
747
        assert_eq!(name.as_str(), "updated");
1✔
748
        Ok(())
1✔
749
    }
1✔
750

751
    #[test]
752
    fn test_structurally_eq_to_self() -> PyResult<()> {
1✔
753
        let exprs = [
1✔
754
            Expr::Var(Var::Bit {
1✔
755
                bit: ShareableClbit::new_anonymous(),
1✔
756
            }),
1✔
757
            Expr::Var(Var::Register {
1✔
758
                register: ClassicalRegister::new_owning("a", 3),
1✔
759
                ty: Type::Uint(3),
1✔
760
            }),
1✔
761
            Expr::Value(Value::Uint {
1✔
762
                raw: 3,
1✔
763
                ty: Type::Uint(2),
1✔
764
            }),
1✔
765
            Expr::Cast(
1✔
766
                Cast {
1✔
767
                    operand: Expr::Var(Var::Register {
1✔
768
                        register: ClassicalRegister::new_owning("a", 3),
1✔
769
                        ty: Type::Uint(3),
1✔
770
                    }),
1✔
771
                    ty: Type::Bool,
1✔
772
                    constant: false,
1✔
773
                    implicit: false,
1✔
774
                }
1✔
775
                .into(),
1✔
776
            ),
1✔
777
            Expr::Unary(
1✔
778
                Unary {
1✔
779
                    op: UnaryOp::LogicNot,
1✔
780
                    operand: Expr::Var(Var::Bit {
1✔
781
                        bit: ShareableClbit::new_anonymous(),
1✔
782
                    }),
1✔
783
                    ty: Type::Bool,
1✔
784
                    constant: false,
1✔
785
                }
1✔
786
                .into(),
1✔
787
            ),
1✔
788
            Expr::Binary(
1✔
789
                Binary {
1✔
790
                    op: BinaryOp::BitAnd,
1✔
791
                    left: Expr::Value(Value::Uint {
1✔
792
                        raw: 5,
1✔
793
                        ty: Type::Uint(3),
1✔
794
                    }),
1✔
795
                    right: Expr::Var(Var::Register {
1✔
796
                        register: ClassicalRegister::new_owning("a", 3),
1✔
797
                        ty: Type::Uint(3),
1✔
798
                    }),
1✔
799
                    ty: Type::Uint(3),
1✔
800
                    constant: false,
1✔
801
                }
1✔
802
                .into(),
1✔
803
            ),
1✔
804
            Expr::Binary(
1✔
805
                Binary {
1✔
806
                    op: BinaryOp::LogicAnd,
1✔
807
                    left: Expr::Binary(
1✔
808
                        Binary {
1✔
809
                            op: BinaryOp::Less,
1✔
810
                            left: Expr::Value(Value::Uint {
1✔
811
                                raw: 2,
1✔
812
                                ty: Type::Uint(3),
1✔
813
                            }),
1✔
814
                            right: Expr::Var(Var::Register {
1✔
815
                                register: ClassicalRegister::new_owning("a", 3),
1✔
816
                                ty: Type::Uint(3),
1✔
817
                            }),
1✔
818
                            ty: Type::Bool,
1✔
819
                            constant: false,
1✔
820
                        }
1✔
821
                        .into(),
1✔
822
                    ),
1✔
823
                    right: Expr::Var(Var::Bit {
1✔
824
                        bit: ShareableClbit::new_anonymous(),
1✔
825
                    }),
1✔
826
                    ty: Type::Bool,
1✔
827
                    constant: false,
1✔
828
                }
1✔
829
                .into(),
1✔
830
            ),
1✔
831
            Expr::Binary(
1✔
832
                Binary {
1✔
833
                    op: BinaryOp::ShiftLeft,
1✔
834
                    left: Expr::Binary(
1✔
835
                        Binary {
1✔
836
                            op: BinaryOp::ShiftRight,
1✔
837
                            left: Expr::Value(Value::Uint {
1✔
838
                                raw: 255,
1✔
839
                                ty: Type::Uint(8),
1✔
840
                            }),
1✔
841
                            right: Expr::Value(Value::Uint {
1✔
842
                                raw: 3,
1✔
843
                                ty: Type::Uint(8),
1✔
844
                            }),
1✔
845
                            ty: Type::Uint(8),
1✔
846
                            constant: true,
1✔
847
                        }
1✔
848
                        .into(),
1✔
849
                    ),
1✔
850
                    right: Expr::Value(Value::Uint {
1✔
851
                        raw: 3,
1✔
852
                        ty: Type::Uint(8),
1✔
853
                    }),
1✔
854
                    ty: Type::Uint(8),
1✔
855
                    constant: true,
1✔
856
                }
1✔
857
                .into(),
1✔
858
            ),
1✔
859
            Expr::Index(
1✔
860
                Index {
1✔
861
                    target: Expr::Var(Var::Standalone {
1✔
862
                        uuid: Uuid::new_v4().as_u128(),
1✔
863
                        name: "a".to_string(),
1✔
864
                        ty: Type::Uint(8),
1✔
865
                    }),
1✔
866
                    index: Expr::Value(Value::Uint {
1✔
867
                        raw: 0,
1✔
868
                        ty: Type::Uint(8),
1✔
869
                    }),
1✔
870
                    ty: Type::Uint(1),
1✔
871
                    constant: false,
1✔
872
                }
1✔
873
                .into(),
1✔
874
            ),
1✔
875
            Expr::Binary(
1✔
876
                Binary {
1✔
877
                    op: BinaryOp::Greater,
1✔
878
                    left: Expr::Stretch(Stretch {
1✔
879
                        uuid: Uuid::new_v4().as_u128(),
1✔
880
                        name: "a".to_string(),
1✔
881
                    }),
1✔
882
                    right: Expr::Value(Value::Duration(Duration::dt(100))),
1✔
883
                    ty: Type::Bool,
1✔
884
                    constant: false,
1✔
885
                }
1✔
886
                .into(),
1✔
887
            ),
1✔
888
        ];
1✔
889

890
        for expr in exprs.iter() {
10✔
891
            assert!(expr.structurally_equivalent(expr));
10✔
892
        }
893

894
        Ok(())
1✔
895
    }
1✔
896

897
    #[test]
898
    fn test_structurally_eq_does_not_compare_symmetrically() {
1✔
899
        let all_ops = [
1✔
900
            BinaryOp::BitAnd,
1✔
901
            BinaryOp::BitOr,
1✔
902
            BinaryOp::BitXor,
1✔
903
            BinaryOp::LogicAnd,
1✔
904
            BinaryOp::LogicOr,
1✔
905
            BinaryOp::Equal,
1✔
906
            BinaryOp::NotEqual,
1✔
907
            BinaryOp::Less,
1✔
908
            BinaryOp::LessEqual,
1✔
909
            BinaryOp::Greater,
1✔
910
            BinaryOp::GreaterEqual,
1✔
911
            BinaryOp::ShiftLeft,
1✔
912
            BinaryOp::ShiftRight,
1✔
913
            BinaryOp::Add,
1✔
914
            BinaryOp::Sub,
1✔
915
            BinaryOp::Mul,
1✔
916
            BinaryOp::Div,
1✔
917
        ];
1✔
918

919
        for op in all_ops.iter().copied() {
17✔
920
            let (left, right, out_ty) = match op {
17✔
921
                BinaryOp::LogicAnd | BinaryOp::LogicOr => (
2✔
922
                    Expr::Value(Value::Uint {
2✔
923
                        raw: 1,
2✔
924
                        ty: Type::Bool,
2✔
925
                    }),
2✔
926
                    Expr::Var(Var::Bit {
2✔
927
                        bit: ShareableClbit::new_anonymous(),
2✔
928
                    }),
2✔
929
                    Type::Bool,
2✔
930
                ),
2✔
931
                _ => (
932
                    Expr::Value(Value::Uint {
15✔
933
                        raw: 5,
15✔
934
                        ty: Type::Uint(3),
15✔
935
                    }),
15✔
936
                    Expr::Var(Var::Register {
15✔
937
                        register: ClassicalRegister::new_owning("a", 3),
15✔
938
                        ty: Type::Uint(3),
15✔
939
                    }),
15✔
940
                    match op {
15✔
941
                        BinaryOp::BitAnd | BinaryOp::BitOr | BinaryOp::BitXor => Type::Uint(3),
3✔
942
                        _ => Type::Bool,
12✔
943
                    },
944
                ),
945
            };
946

947
            let cis = Expr::Binary(
17✔
948
                Binary {
17✔
949
                    op,
17✔
950
                    left: left.clone(),
17✔
951
                    right: right.clone(),
17✔
952
                    ty: out_ty,
17✔
953
                    constant: false,
17✔
954
                }
17✔
955
                .into(),
17✔
956
            );
17✔
957

958
            let trans = Expr::Binary(
17✔
959
                Binary {
17✔
960
                    op,
17✔
961
                    left: right,
17✔
962
                    right: left,
17✔
963
                    ty: out_ty,
17✔
964
                    constant: false,
17✔
965
                }
17✔
966
                .into(),
17✔
967
            );
17✔
968

969
            assert!(
17✔
970
                !cis.structurally_equivalent(&trans),
17✔
NEW
971
                "Expected {op:?} to not be structurally equivalent to its flipped form"
×
972
            );
973
            assert!(
17✔
974
                !trans.structurally_equivalent(&cis),
17✔
NEW
975
                "Expected flipped {op:?} to not be structurally equivalent to original form"
×
976
            );
977
        }
978
    }
1✔
979

980
    #[test]
981
    fn test_structurally_eq_key_function_both() -> PyResult<()> {
1✔
982
        let left_clbit = ShareableClbit::new_anonymous();
1✔
983
        let left_cr = ClassicalRegister::new_owning("a", 3);
1✔
984
        let right_clbit = ShareableClbit::new_anonymous();
1✔
985
        let right_cr = ClassicalRegister::new_owning("b", 3);
1✔
986
        assert_ne!(left_clbit, right_clbit);
1✔
987
        assert_ne!(left_cr, right_cr);
1✔
988

989
        let left = Expr::Unary(
1✔
990
            Unary {
1✔
991
                op: UnaryOp::LogicNot,
1✔
992
                operand: Expr::Binary(
1✔
993
                    Binary {
1✔
994
                        op: BinaryOp::LogicAnd,
1✔
995
                        left: Expr::Binary(
1✔
996
                            Binary {
1✔
997
                                op: BinaryOp::Less,
1✔
998
                                left: Expr::Value(Value::Uint {
1✔
999
                                    raw: 5,
1✔
1000
                                    ty: Type::Uint(3),
1✔
1001
                                }),
1✔
1002
                                right: Expr::Var(Var::Register {
1✔
1003
                                    register: left_cr.clone(),
1✔
1004
                                    ty: Type::Uint(3),
1✔
1005
                                }),
1✔
1006
                                ty: Type::Bool,
1✔
1007
                                constant: false,
1✔
1008
                            }
1✔
1009
                            .into(),
1✔
1010
                        ),
1✔
1011
                        right: Expr::Var(Var::Bit {
1✔
1012
                            bit: left_clbit.clone(),
1✔
1013
                        }),
1✔
1014
                        ty: Type::Bool,
1✔
1015
                        constant: false,
1✔
1016
                    }
1✔
1017
                    .into(),
1✔
1018
                ),
1✔
1019
                ty: Type::Bool,
1✔
1020
                constant: false,
1✔
1021
            }
1✔
1022
            .into(),
1✔
1023
        );
1✔
1024

1025
        let right = Expr::Unary(
1✔
1026
            Unary {
1✔
1027
                op: UnaryOp::LogicNot,
1✔
1028
                operand: Expr::Binary(
1✔
1029
                    Binary {
1✔
1030
                        op: BinaryOp::LogicAnd,
1✔
1031
                        left: Expr::Binary(
1✔
1032
                            Binary {
1✔
1033
                                op: BinaryOp::Less,
1✔
1034
                                left: Expr::Value(Value::Uint {
1✔
1035
                                    raw: 5,
1✔
1036
                                    ty: Type::Uint(3),
1✔
1037
                                }),
1✔
1038
                                right: Expr::Var(Var::Register {
1✔
1039
                                    register: right_cr.clone(),
1✔
1040
                                    ty: Type::Uint(3),
1✔
1041
                                }),
1✔
1042
                                ty: Type::Bool,
1✔
1043
                                constant: false,
1✔
1044
                            }
1✔
1045
                            .into(),
1✔
1046
                        ),
1✔
1047
                        right: Expr::Var(Var::Bit {
1✔
1048
                            bit: right_clbit.clone(),
1✔
1049
                        }),
1✔
1050
                        ty: Type::Bool,
1✔
1051
                        constant: false,
1✔
1052
                    }
1✔
1053
                    .into(),
1✔
1054
                ),
1✔
1055
                ty: Type::Bool,
1✔
1056
                constant: false,
1✔
1057
            }
1✔
1058
            .into(),
1✔
1059
        );
1✔
1060

1061
        assert!(
1✔
1062
            !left.structurally_equivalent(&right),
1✔
1063
            "Expressions using different registers/clbits should not be structurally equivalent"
×
1064
        );
1065

1066
        // We're happy as long as the variables are of the same kind.
1067
        let key_func = |v: &Var| -> &str {
4✔
1068
            match v {
4✔
1069
                Var::Standalone { .. } => "standalone",
×
1070
                Var::Bit { .. } => "bit",
2✔
1071
                Var::Register { .. } => "register",
2✔
1072
            }
1073
        };
4✔
1074

1075
        assert!(
1✔
1076
            left.structurally_equivalent_by_key(key_func, &right, key_func),
1✔
1077
            "Expressions that only care that their vars are of the same kind should be equivalent"
×
1078
        );
1079

1080
        Ok(())
1✔
1081
    }
1✔
1082
}
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