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

Qiskit / qiskit / 18718499784

22 Oct 2025 01:51PM UTC coverage: 88.21% (-0.004%) from 88.214%
18718499784

push

github

web-flow
Update `nom` to version 8 from 7 (#15204)

This was a breaking API change that required a simultaneous update to
`nom-unicode`, and some of the `nom` code was moved to a new
`nom-language` crate.

The main change here is in how parsers are invoked (we call
`Parser::parse` now instead of invoking the function directly), and how
combinators on the results of parsers are applied - the suggestion is to
use things like `map_res` via the `Parser` trait instead of the old
individual functions.

Several uses of `map_res` with infallible lambdas in the parser were
changed to use `map`.

82 of 95 new or added lines in 1 file covered. (86.32%)

22 existing lines in 4 files now uncovered.

93436 of 105924 relevant lines covered (88.21%)

1165529.64 hits per line

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

81.99
/crates/circuit/src/parameter/parameter_expression.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2023, 2024
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 std::sync::Arc;
14

15
use hashbrown::hash_map::Entry;
16
use hashbrown::{HashMap, HashSet};
17
use num_complex::Complex64;
18
use pyo3::exceptions::{PyRuntimeError, PyTypeError, PyValueError, PyZeroDivisionError};
19
use pyo3::types::{IntoPyDict, PyComplex, PyFloat, PyInt, PyNotImplemented, PySet, PyString};
20
use thiserror::Error;
21
use uuid::Uuid;
22

23
use std::collections::hash_map::DefaultHasher;
24
use std::fmt;
25
use std::hash::{Hash, Hasher};
26

27
use pyo3::IntoPyObjectExt;
28
use pyo3::prelude::*;
29

30
use crate::circuit_data::CircuitError;
31
use crate::imports::{BUILTIN_HASH, SYMPIFY_PARAMETER_EXPRESSION, UUID};
32
use crate::parameter::symbol_expr;
33
use crate::parameter::symbol_expr::SymbolExpr;
34
use crate::parameter::symbol_parser::parse_expression;
35

36
use super::symbol_expr::{SYMEXPR_EPSILON, Symbol, Value};
37

38
/// Errors for dealing with parameters and parameter expressions.
39
#[derive(Error, Debug)]
40
pub enum ParameterError {
41
    #[error("Division by zero.")]
42
    ZeroDivisionError,
43
    #[error("Binding to infinite value.")]
44
    BindingInf,
45
    #[error("Binding to NaN.")]
46
    BindingNaN,
47
    #[error("Invalid value: NaN or infinite.")]
48
    InvalidValue,
49
    #[error("Cannot bind following parameters not present in expression: {0:?}")]
50
    UnknownParameters(HashSet<Symbol>),
51
    #[error("Parameter expression with unbound parameters {0:?} is not numeric.")]
52
    UnboundParameters(HashSet<Symbol>),
53
    #[error("Name conflict adding parameters.")]
54
    NameConflict,
55
    #[error("Invalid cast to OpCode: {0}")]
56
    InvalidU8ToOpCode(u8),
57
    #[error("Could not cast to Symbol.")]
58
    NotASymbol,
59
    #[error("Derivative not supported on expression: {0}")]
60
    DerivativeNotSupported(String),
61
}
62

63
impl From<ParameterError> for PyErr {
64
    fn from(value: ParameterError) -> Self {
3,478✔
65
        match value {
3,478✔
66
            ParameterError::ZeroDivisionError => {
67
                PyZeroDivisionError::new_err("zero division occurs while binding parameter")
1,012✔
68
            }
69
            ParameterError::BindingInf => {
70
                PyZeroDivisionError::new_err("attempted to bind infinite value to parameter")
1,828✔
71
            }
72
            ParameterError::UnknownParameters(_) | ParameterError::NameConflict => {
73
                CircuitError::new_err(value.to_string())
12✔
74
            }
75
            ParameterError::UnboundParameters(_) => PyTypeError::new_err(value.to_string()),
582✔
76
            ParameterError::InvalidValue => PyValueError::new_err(value.to_string()),
40✔
77
            _ => PyRuntimeError::new_err(value.to_string()),
4✔
78
        }
79
    }
3,478✔
80
}
81

82
/// A parameter expression.
83
///
84
/// This is backed by Qiskit's symbolic expression engine and a cache
85
/// for the parameters inside the expression.
86
#[derive(Clone, Debug)]
87
pub struct ParameterExpression {
88
    // The symbolic expression.
89
    expr: SymbolExpr,
90
    // A map keeping track of all symbols, with their name. This map *must* have
91
    // exactly one entry per symbol used in the expression (no more, no less).
92
    name_map: HashMap<String, Symbol>,
93
}
94

95
impl Hash for ParameterExpression {
96
    fn hash<H: Hasher>(&self, state: &mut H) {
×
97
        // The string representation of a tree is unique.
98
        self.expr.string_id().hash(state);
×
99
    }
×
100
}
101

102
impl PartialEq for ParameterExpression {
103
    fn eq(&self, other: &Self) -> bool {
17,894✔
104
        self.expr.eq(&other.expr)
17,894✔
105
    }
17,894✔
106
}
107

108
impl Eq for ParameterExpression {}
109

110
impl Default for ParameterExpression {
111
    /// The default constructor returns zero.
112
    fn default() -> Self {
×
113
        Self {
×
114
            expr: SymbolExpr::Value(Value::Int(0)),
×
115
            name_map: HashMap::new(), // no parameters, hence empty name map
×
116
        }
×
117
    }
×
118
}
119

120
impl fmt::Display for ParameterExpression {
121
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336,816✔
122
        write!(f, "{}", {
336,816✔
123
            if let SymbolExpr::Symbol(s) = &self.expr {
336,816✔
124
                s.repr(false)
192,758✔
125
            } else {
126
                match self.expr.eval(true) {
144,058✔
127
                    Some(e) => e.to_string(),
184✔
128
                    None => self.expr.to_string(),
143,874✔
129
                }
130
            }
131
        })
132
    }
336,816✔
133
}
134

135
// This needs to be implemented manually, because PyO3 does not provide built-in
136
// conversions for the subclasses of ParameterExpression in Python. Specifically
137
// the Python classes Parameter and ParameterVector are subclasses of
138
// ParameterExpression and the default trait impl would not handle the specialization
139
// there.
140
impl<'py> IntoPyObject<'py> for ParameterExpression {
141
    type Target = PyParameterExpression;
142
    type Output = Bound<'py, Self::Target>;
143
    type Error = PyErr;
144

145
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
×
146
        let expr = PyParameterExpression::from(self.clone());
×
147
        expr.into_pyobject(py)
×
148
    }
×
149
}
150

151
/// Lookup for which operations are binary (i.e. require two operands).
152
static BINARY_OPS: [OpCode; 8] = [
153
    // a HashSet would be better but requires unstable features
154
    OpCode::ADD,
155
    OpCode::SUB,
156
    OpCode::MUL,
157
    OpCode::DIV,
158
    OpCode::POW,
159
    OpCode::RSUB,
160
    OpCode::RDIV,
161
    OpCode::RPOW,
162
];
163

164
impl ParameterExpression {
165
    /// Initialize with an existing [SymbolExpr] and its valid name map.
166
    ///
167
    /// Caution: The caller **guarantees** that ``name_map`` is consistent with ``expr``.
168
    /// If uncertain, call [Self::from_symbol_expr], which automatically builds the correct name map.
169
    pub fn new(expr: SymbolExpr, name_map: HashMap<String, Symbol>) -> Self {
2,509,556✔
170
        Self { expr, name_map }
2,509,556✔
171
    }
2,509,556✔
172

173
    /// Construct from a [Symbol].
174
    pub fn from_symbol(symbol: Symbol) -> Self {
76,336✔
175
        Self {
76,336✔
176
            expr: SymbolExpr::Symbol(Arc::new(symbol.clone())),
76,336✔
177
            name_map: [(symbol.repr(false), symbol)].into(),
76,336✔
178
        }
76,336✔
179
    }
76,336✔
180

181
    /// Try casting to a [Symbol].
182
    ///
183
    /// This only succeeds if the underlying expression is, in fact, only a symbol.
184
    pub fn try_to_symbol(&self) -> Result<Symbol, ParameterError> {
249,714✔
185
        if let SymbolExpr::Symbol(symbol) = &self.expr {
249,714✔
186
            Ok(symbol.as_ref().clone())
240,120✔
187
        } else {
188
            Err(ParameterError::NotASymbol)
9,594✔
189
        }
190
    }
249,714✔
191

192
    /// Try casting to a [Symbol], returning a reference.
193
    ///
194
    /// This only succeeds if the underlying expression is, in fact, only a symbol.
195
    pub fn try_to_symbol_ref(&self) -> Result<&Symbol, ParameterError> {
×
196
        if let SymbolExpr::Symbol(symbol) = &self.expr {
×
197
            Ok(symbol.as_ref())
×
198
        } else {
199
            Err(ParameterError::NotASymbol)
×
200
        }
201
    }
×
202

203
    /// Try casting to a [Value].
204
    ///
205
    /// Attempt to evaluate the expression recursively and return a [Value] if fully bound.
206
    ///
207
    /// # Arguments
208
    ///
209
    /// * strict - If ``true``, only allow returning a value if all symbols are bound. If
210
    ///   ``false``, allow casting expressions to values, even though symbols might still exist.
211
    ///   For example, ``0 * x`` will return ``0`` for ``strict=false`` and otherwise return
212
    ///   an error.
213
    pub fn try_to_value(&self, strict: bool) -> Result<Value, ParameterError> {
9,639,634✔
214
        if strict && !self.name_map.is_empty() {
9,639,634✔
215
            let free_symbols = self.expr.iter_symbols().cloned().collect();
72,262✔
216
            return Err(ParameterError::UnboundParameters(free_symbols));
72,262✔
217
        }
9,567,372✔
218

219
        match self.expr.eval(true) {
9,567,372✔
220
            Some(value) => {
401,802✔
221
                // we try to restrict complex to real, if possible
222
                if let Value::Complex(c) = value {
401,802✔
223
                    if (-symbol_expr::SYMEXPR_EPSILON..symbol_expr::SYMEXPR_EPSILON).contains(&c.im)
41,550✔
224
                    {
225
                        return Ok(Value::Real(c.re));
×
226
                    }
41,550✔
227
                }
360,252✔
228
                Ok(value)
401,802✔
229
            }
230
            None => {
231
                let free_symbols = self.expr.iter_symbols().cloned().collect();
9,165,570✔
232
                Err(ParameterError::UnboundParameters(free_symbols))
9,165,570✔
233
            }
234
        }
235
    }
9,639,634✔
236

237
    /// Construct from a [SymbolExpr].
238
    ///
239
    /// This populates the name map with the symbols in the expression.
240
    pub fn from_symbol_expr(expr: SymbolExpr) -> Self {
4,765,076✔
241
        let name_map = expr.name_map();
4,765,076✔
242
        Self { expr, name_map }
4,765,076✔
243
    }
4,765,076✔
244

245
    /// Initialize from an f64.
246
    pub fn from_f64(value: f64) -> Self {
25,012✔
247
        Self {
25,012✔
248
            expr: SymbolExpr::Value(Value::Real(value)),
25,012✔
249
            name_map: HashMap::new(),
25,012✔
250
        }
25,012✔
251
    }
25,012✔
252

253
    /// Load from a sequence of [OPReplay]s. Used in serialization.
254
    pub fn from_qpy(replay: &[OPReplay]) -> Result<Self, ParameterError> {
×
255
        // the stack contains the latest lhs and rhs values
256
        let mut stack: Vec<ParameterExpression> = Vec::new();
×
257

258
        for inst in replay.iter() {
×
259
            let OPReplay { op, lhs, rhs } = inst;
×
260

261
            // put the values on the stack, if they exist
262
            if let Some(value) = lhs {
×
263
                stack.push(value.clone().into());
×
264
            }
×
265
            if let Some(value) = rhs {
×
266
                stack.push(value.clone().into());
×
267
            }
×
268

269
            // if we need two operands, pop rhs from the stack
270
            let rhs = if BINARY_OPS.contains(op) {
×
271
                Some(stack.pop().expect("Pop from empty stack"))
×
272
            } else {
273
                None
×
274
            };
275

276
            // pop lhs from the stack, this we always need
277
            let lhs = stack.pop().expect("Pop from empty stack");
×
278

279
            // apply the operation and put the result onto the stack for the next replay
280
            let result: ParameterExpression = match op {
×
281
                OpCode::ADD => lhs.add(&rhs.unwrap())?,
×
282
                OpCode::MUL => lhs.mul(&rhs.unwrap())?,
×
283
                OpCode::SUB => lhs.sub(&rhs.unwrap())?,
×
284
                OpCode::RSUB => rhs.unwrap().sub(&lhs)?,
×
285
                OpCode::POW => lhs.pow(&rhs.unwrap())?,
×
286
                OpCode::RPOW => rhs.unwrap().pow(&lhs)?,
×
287
                OpCode::DIV => lhs.div(&rhs.unwrap())?,
×
288
                OpCode::RDIV => rhs.unwrap().div(&lhs)?,
×
289
                OpCode::ABS => lhs.abs(),
×
290
                OpCode::SIN => lhs.sin(),
×
291
                OpCode::ASIN => lhs.asin(),
×
292
                OpCode::COS => lhs.cos(),
×
293
                OpCode::ACOS => lhs.acos(),
×
294
                OpCode::TAN => lhs.tan(),
×
295
                OpCode::ATAN => lhs.atan(),
×
296
                OpCode::CONJ => lhs.conjugate(),
×
297
                OpCode::LOG => lhs.log(),
×
298
                OpCode::EXP => lhs.exp(),
×
299
                OpCode::SIGN => lhs.sign(),
×
300
                OpCode::GRAD | OpCode::SUBSTITUTE => {
301
                    panic!("GRAD and SUBSTITUTE are not supported.")
×
302
                }
303
            };
304
            stack.push(result);
×
305
        }
306

307
        // once we're done, just return the last element in the stack
308
        Ok(stack
×
309
            .pop()
×
310
            .expect("Invalid QPY replay encountered during deserialization: empty OPReplay."))
×
311
    }
×
312

313
    pub fn iter_symbols(&self) -> impl Iterator<Item = &Symbol> + '_ {
294,566✔
314
        self.name_map.values()
294,566✔
315
    }
294,566✔
316

317
    /// Get the number of [Symbol]s in the expression.
318
    pub fn num_symbols(&self) -> usize {
×
319
        self.name_map.len()
×
320
    }
×
321

322
    /// Whether the expression represents a complex number. None if cannot be determined.
323
    pub fn is_complex(&self) -> Option<bool> {
23,080✔
324
        self.expr.is_complex()
23,080✔
325
    }
23,080✔
326

327
    /// Whether the expression represents a int. None if cannot be determined.
328
    pub fn is_int(&self) -> Option<bool> {
85,612✔
329
        self.expr.is_int()
85,612✔
330
    }
85,612✔
331

332
    /// Add an expression; ``self + rhs``.
333
    pub fn add(&self, rhs: &ParameterExpression) -> Result<Self, ParameterError> {
2,390,418✔
334
        let name_map = self.merged_name_map(rhs)?;
2,390,418✔
335
        Ok(Self {
2,390,416✔
336
            expr: &self.expr + &rhs.expr,
2,390,416✔
337
            name_map,
2,390,416✔
338
        })
2,390,416✔
339
    }
2,390,418✔
340

341
    /// Multiply with an expression; ``self * rhs``.
342
    pub fn mul(&self, rhs: &ParameterExpression) -> Result<Self, ParameterError> {
2,414,214✔
343
        let name_map = self.merged_name_map(rhs)?;
2,414,214✔
344
        Ok(Self {
2,414,212✔
345
            expr: &self.expr * &rhs.expr,
2,414,212✔
346
            name_map,
2,414,212✔
347
        })
2,414,212✔
348
    }
2,414,214✔
349

350
    /// Subtract another expression; ``self - rhs``.
351
    pub fn sub(&self, rhs: &ParameterExpression) -> Result<Self, ParameterError> {
23,184✔
352
        let name_map = self.merged_name_map(rhs)?;
23,184✔
353
        Ok(Self {
23,182✔
354
            expr: &self.expr - &rhs.expr,
23,182✔
355
            name_map,
23,182✔
356
        })
23,182✔
357
    }
23,184✔
358

359
    /// Divide by another expression; ``self / rhs``.
360
    pub fn div(&self, rhs: &ParameterExpression) -> Result<Self, ParameterError> {
12,956✔
361
        if rhs.expr.is_zero() {
12,956✔
362
            return Err(ParameterError::ZeroDivisionError);
1,012✔
363
        }
11,944✔
364

365
        let name_map = self.merged_name_map(rhs)?;
11,944✔
366
        Ok(Self {
11,942✔
367
            expr: &self.expr / &rhs.expr,
11,942✔
368
            name_map,
11,942✔
369
        })
11,942✔
370
    }
12,956✔
371

372
    /// Raise this expression to a power; ``self ^ rhs``.
373
    pub fn pow(&self, rhs: &ParameterExpression) -> Result<Self, ParameterError> {
3,016✔
374
        let name_map = self.merged_name_map(rhs)?;
3,016✔
375
        Ok(Self {
3,016✔
376
            expr: self.expr.pow(&rhs.expr),
3,016✔
377
            name_map,
3,016✔
378
        })
3,016✔
379
    }
3,016✔
380

381
    /// Apply the sine to this expression; ``sin(self)``.
382
    pub fn sin(&self) -> Self {
362✔
383
        Self {
362✔
384
            expr: self.expr.sin(),
362✔
385
            name_map: self.name_map.clone(),
362✔
386
        }
362✔
387
    }
362✔
388

389
    /// Apply the cosine to this expression; ``cos(self)``.
390
    pub fn cos(&self) -> Self {
352✔
391
        Self {
352✔
392
            expr: self.expr.cos(),
352✔
393
            name_map: self.name_map.clone(),
352✔
394
        }
352✔
395
    }
352✔
396

397
    /// Apply the tangent to this expression; ``tan(self)``.
398
    pub fn tan(&self) -> Self {
352✔
399
        Self {
352✔
400
            expr: self.expr.tan(),
352✔
401
            name_map: self.name_map.clone(),
352✔
402
        }
352✔
403
    }
352✔
404

405
    /// Apply the arcsine to this expression; ``asin(self)``.
406
    pub fn asin(&self) -> Self {
100✔
407
        Self {
100✔
408
            expr: self.expr.asin(),
100✔
409
            name_map: self.name_map.clone(),
100✔
410
        }
100✔
411
    }
100✔
412

413
    /// Apply the arccosine to this expression; ``acos(self)``.
414
    pub fn acos(&self) -> Self {
100✔
415
        Self {
100✔
416
            expr: self.expr.acos(),
100✔
417
            name_map: self.name_map.clone(),
100✔
418
        }
100✔
419
    }
100✔
420

421
    /// Apply the arctangent to this expression; ``atan(self)``.
422
    pub fn atan(&self) -> Self {
100✔
423
        Self {
100✔
424
            expr: self.expr.atan(),
100✔
425
            name_map: self.name_map.clone(),
100✔
426
        }
100✔
427
    }
100✔
428

429
    /// Exponentiate this expression; ``exp(self)``.
430
    pub fn exp(&self) -> Self {
350✔
431
        Self {
350✔
432
            expr: self.expr.exp(),
350✔
433
            name_map: self.name_map.clone(),
350✔
434
        }
350✔
435
    }
350✔
436

437
    /// Take the (natural) logarithm of this expression; ``log(self)``.
438
    pub fn log(&self) -> Self {
266✔
439
        Self {
266✔
440
            expr: self.expr.log(),
266✔
441
            name_map: self.name_map.clone(),
266✔
442
        }
266✔
443
    }
266✔
444

445
    /// Take the absolute value of this expression; ``|self|``.
446
    pub fn abs(&self) -> Self {
374✔
447
        Self {
374✔
448
            expr: self.expr.abs(),
374✔
449
            name_map: self.name_map.clone(),
374✔
450
        }
374✔
451
    }
374✔
452

453
    /// Return the sign of this expression; ``sign(self)``.
454
    pub fn sign(&self) -> Self {
10✔
455
        Self {
10✔
456
            expr: self.expr.sign(),
10✔
457
            name_map: self.name_map.clone(),
10✔
458
        }
10✔
459
    }
10✔
460

461
    /// Complex conjugate the expression.
462
    pub fn conjugate(&self) -> Self {
1,832✔
463
        Self {
1,832✔
464
            expr: self.expr.conjugate(),
1,832✔
465
            name_map: self.name_map.clone(),
1,832✔
466
        }
1,832✔
467
    }
1,832✔
468

469
    /// negate the expression.
470
    pub fn neg(&self) -> Self {
×
471
        Self {
×
472
            expr: -&self.expr,
×
473
            name_map: self.name_map.clone(),
×
474
        }
×
475
    }
×
476

477
    /// Compute the derivative of the expression with respect to the provided symbol.
478
    ///
479
    /// Note that this keeps the name map unchanged. Meaning that computing the derivative
480
    /// of ``x`` will yield ``1`` but the expression still owns the symbol ``x``. This is
481
    /// done such that we can still bind the value ``x`` in an automated process.
482
    pub fn derivative(&self, param: &Symbol) -> Result<Self, ParameterError> {
74✔
483
        Ok(Self {
484
            expr: self
74✔
485
                .expr
74✔
486
                .derivative(param)
74✔
487
                .map_err(ParameterError::DerivativeNotSupported)?,
74✔
488
            name_map: self.name_map.clone(),
70✔
489
        })
490
    }
74✔
491

492
    /// Substitute symbols with [ParameterExpression]s.
493
    ///
494
    /// # Arguments
495
    ///
496
    /// * map - A hashmap with [Symbol] keys and [ParameterExpression]s to replace these
497
    ///   symbols with.
498
    /// * allow_unknown_parameters - If `false`, returns an error if any symbol in the
499
    ///   hashmap is not present in the expression. If `true`, unknown symbols are ignored.
500
    ///   Setting to `true` is slightly faster as it does not involve additional checks.
501
    ///
502
    /// # Returns
503
    ///
504
    /// * `Ok(Self)` - A parameter expression with the substituted expressions.
505
    /// * `Err(ParameterError)` - An error if the subtitution failed.
506
    pub fn subs(
233,662✔
507
        &self,
233,662✔
508
        map: &HashMap<Symbol, Self>,
233,662✔
509
        allow_unknown_parameters: bool,
233,662✔
510
    ) -> Result<Self, ParameterError> {
233,662✔
511
        // Build the outgoing name map. In the process we check for any duplicates.
512
        let mut name_map: HashMap<String, Symbol> = HashMap::new();
233,662✔
513
        let mut symbol_map: HashMap<Symbol, SymbolExpr> = HashMap::new();
233,662✔
514

515
        // If we don't allow for unknown parameters, check if there are any.
516
        if !allow_unknown_parameters {
233,662✔
517
            let existing: HashSet<&Symbol> = self.name_map.values().collect();
36,074✔
518
            let to_replace: HashSet<&Symbol> = map.keys().collect();
36,074✔
519
            let mut difference = to_replace.difference(&existing).peekable();
36,074✔
520

521
            if difference.peek().is_some() {
36,074✔
522
                let different_symbols = difference.map(|s| (**s).clone()).collect();
2✔
523
                return Err(ParameterError::UnknownParameters(different_symbols));
2✔
524
            }
36,072✔
525
        }
197,588✔
526

527
        for (name, symbol) in self.name_map.iter() {
242,950✔
528
            // check if the symbol will get replaced
529
            if let Some(replacement) = map.get(symbol) {
242,950✔
530
                // If yes, update the name_map. This also checks for duplicates.
531
                for (replacement_name, replacement_symbol) in replacement.name_map.iter() {
38,564✔
532
                    if let Some(duplicate) = name_map.get(replacement_name) {
38,564✔
533
                        // If a symbol with the same name already exists, check whether it is
534
                        // the same symbol (fine) or a different symbol with the same name (conflict)!
535
                        if duplicate != replacement_symbol {
32✔
536
                            return Err(ParameterError::NameConflict);
2✔
537
                        }
30✔
538
                    } else {
38,532✔
539
                        // SAFETY: We know the key does not exist yet.
38,532✔
540
                        unsafe {
38,532✔
541
                            name_map.insert_unique_unchecked(
38,532✔
542
                                replacement_name.clone(),
38,532✔
543
                                replacement_symbol.clone(),
38,532✔
544
                            )
38,532✔
545
                        };
38,532✔
546
                    }
38,532✔
547
                }
548

549
                // If we got until here, there were no duplicates, so we are safe to
550
                // add this symbol to the internal replacement map.
551
                symbol_map.insert(symbol.clone(), replacement.expr.clone());
37,458✔
552
            } else {
553
                // no replacement for this symbol, carry on
554
                match name_map.entry(name.clone()) {
205,490✔
555
                    Entry::Occupied(duplicate) => {
22✔
556
                        if duplicate.get() != symbol {
22✔
UNCOV
557
                            return Err(ParameterError::NameConflict);
×
558
                        }
22✔
559
                    }
560
                    Entry::Vacant(e) => {
205,468✔
561
                        e.insert(symbol.clone());
205,468✔
562
                    }
205,468✔
563
                }
564
            }
565
        }
566

567
        let res = self.expr.subs(&symbol_map);
233,658✔
568
        Ok(Self {
233,658✔
569
            expr: res,
233,658✔
570
            name_map,
233,658✔
571
        })
233,658✔
572
    }
233,662✔
573

574
    /// Bind symbols to values.
575
    ///
576
    /// # Arguments
577
    ///
578
    /// * map - A hashmap with [Symbol] keys and [Value]s to replace these
579
    ///   symbols with.
580
    /// * allow_unknown_parameter - If `false`, returns an error if any symbol in the
581
    ///   hashmap is not present in the expression. If `true`, unknown symbols are ignored.
582
    ///   Setting to `true` is slightly faster as it does not involve additional checks.
583
    ///
584
    /// # Returns
585
    ///
586
    /// * `Ok(Self)` - A parameter expression with the bound symbols.
587
    /// * `Err(ParameterError)` - An error if binding failed.
588
    pub fn bind(
402,948✔
589
        &self,
402,948✔
590
        map: &HashMap<&Symbol, Value>,
402,948✔
591
        allow_unknown_parameters: bool,
402,948✔
592
    ) -> Result<Self, ParameterError> {
402,948✔
593
        // The set of symbols we will bind. Used twice, hence pre-computed here.
594
        let bind_symbols: HashSet<&Symbol> = map.keys().cloned().collect();
402,948✔
595

596
        // If we don't allow for unknown parameters, check if there are any.
597
        if !allow_unknown_parameters {
402,948✔
598
            let existing: HashSet<&Symbol> = self.name_map.values().collect();
205,272✔
599
            let mut difference = bind_symbols.difference(&existing).peekable();
205,272✔
600

601
            if difference.peek().is_some() {
205,272✔
602
                let different_symbols = difference.map(|s| (**s).clone()).collect();
×
603
                return Err(ParameterError::UnknownParameters(different_symbols));
×
604
            }
205,272✔
605
        }
197,676✔
606

607
        // bind the symbol expression and then check the outcome for inf/nan, or numeric values
608
        let bound_expr = self.expr.bind(map);
402,948✔
609
        let bound = match bound_expr.eval(true) {
402,948✔
610
            Some(v) => match &v {
400,326✔
611
                Value::Real(r) => {
329,352✔
612
                    if r.is_infinite() {
329,352✔
613
                        Err(ParameterError::BindingInf)
1,828✔
614
                    } else if r.is_nan() {
327,524✔
615
                        Err(ParameterError::BindingNaN)
×
616
                    } else {
617
                        Ok(SymbolExpr::Value(v))
327,524✔
618
                    }
619
                }
620
                Value::Int(_) => Ok(SymbolExpr::Value(v)),
27,894✔
621
                Value::Complex(c) => {
43,080✔
622
                    if c.re.is_infinite() || c.im.is_infinite() {
43,080✔
623
                        Err(ParameterError::BindingInf)
×
624
                    } else if c.re.is_nan() || c.im.is_nan() {
43,080✔
625
                        Err(ParameterError::BindingNaN)
×
626
                    } else if (-symbol_expr::SYMEXPR_EPSILON..symbol_expr::SYMEXPR_EPSILON)
43,080✔
627
                        .contains(&c.im)
43,080✔
628
                    {
629
                        Ok(SymbolExpr::Value(Value::Real(c.re)))
1,728✔
630
                    } else {
631
                        Ok(SymbolExpr::Value(v))
41,352✔
632
                    }
633
                }
634
            },
635
            None => Ok(bound_expr),
2,622✔
636
        }?;
1,828✔
637

638
        // update the name map by removing the bound parameters
639
        let bound_name_map: HashMap<String, Symbol> = self
401,120✔
640
            .name_map
401,120✔
641
            .iter()
401,120✔
642
            .filter(|(_, symbol)| !bind_symbols.contains(symbol))
4,700,088✔
643
            .map(|(name, symbol)| (name.clone(), symbol.clone()))
401,120✔
644
            .collect();
401,120✔
645

646
        Ok(Self {
401,120✔
647
            expr: bound,
401,120✔
648
            name_map: bound_name_map,
401,120✔
649
        })
401,120✔
650
    }
402,948✔
651

652
    /// Merge name maps.
653
    ///
654
    /// # Arguments
655
    ///
656
    /// * `other` - The other parameter expression whose symbols we add to self.
657
    ///
658
    /// # Returns
659
    ///
660
    /// * `Ok(HashMap<String, Symbol>)` - The merged name map.
661
    /// * `Err(ParameterError)` - An error if there was a name conflict.
662
    fn merged_name_map(&self, other: &Self) -> Result<HashMap<String, Symbol>, ParameterError> {
4,842,776✔
663
        let mut merged = self.name_map.clone();
4,842,776✔
664
        for (name, param) in other.name_map.iter() {
7,050,998✔
665
            match merged.get(name) {
7,050,038✔
666
                Some(existing_param) => {
3,990,474✔
667
                    if param != existing_param {
3,990,474✔
668
                        return Err(ParameterError::NameConflict);
8✔
669
                    }
3,990,466✔
670
                }
671
                None => {
3,059,564✔
672
                    // SAFETY: We ensured the key is unique
3,059,564✔
673
                    let _ = unsafe { merged.insert_unique_unchecked(name.clone(), param.clone()) };
3,059,564✔
674
                }
3,059,564✔
675
            }
676
        }
677
        Ok(merged)
4,842,768✔
678
    }
4,842,776✔
679
}
680

681
/// A parameter expression.
682
///
683
/// This is backed by Qiskit's symbolic expression engine and a cache
684
/// for the parameters inside the expression.
685
#[pyclass(
686
    subclass,
687
    module = "qiskit._accelerate.circuit",
688
    name = "ParameterExpression"
689
)]
690
#[derive(Clone, Debug)]
691
pub struct PyParameterExpression {
692
    pub inner: ParameterExpression,
693
}
694

695
impl Default for PyParameterExpression {
696
    /// The default constructor returns zero.
697
    fn default() -> Self {
×
698
        Self {
×
699
            inner: ParameterExpression::default(),
×
700
        }
×
701
    }
×
702
}
703

704
impl fmt::Display for PyParameterExpression {
705
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336,816✔
706
        self.inner.fmt(f)
336,816✔
707
    }
336,816✔
708
}
709

710
impl From<ParameterExpression> for PyParameterExpression {
711
    fn from(value: ParameterExpression) -> Self {
12,333,678✔
712
        Self { inner: value }
12,333,678✔
713
    }
12,333,678✔
714
}
715

716
impl PyParameterExpression {
717
    /// Attempt to extract a `PyParameterExpression` from a bound `PyAny`.
718
    ///
719
    /// This will try to coerce to the strictest data type:
720
    /// Int - Real - Complex - PyParameterVectorElement - PyParameter - PyParameterExpression.
721
    ///
722
    /// # Arguments:
723
    ///
724
    /// * ob - The bound `PyAny` to extract from.
725
    ///
726
    /// # Returns
727
    ///
728
    /// * `Ok(Self)` - The extracted expression.
729
    /// * `Err(PyResult)` - An error if extraction to all above types failed.
730
    pub fn extract_coerce(ob: &Bound<'_, PyAny>) -> PyResult<Self> {
4,960,390✔
731
        if let Ok(i) = ob.downcast::<PyInt>() {
4,960,390✔
732
            Ok(ParameterExpression::new(
134,152✔
733
                SymbolExpr::Value(Value::from(i.extract::<i64>()?)),
134,152✔
734
                HashMap::new(),
134,152✔
735
            )
736
            .into())
134,152✔
737
        } else if let Ok(r) = ob.downcast::<PyFloat>() {
4,826,238✔
738
            let r: f64 = r.extract()?;
13,554✔
739
            if r.is_infinite() || r.is_nan() {
13,554✔
740
                return Err(ParameterError::InvalidValue.into());
40✔
741
            }
13,514✔
742
            Ok(ParameterExpression::new(SymbolExpr::Value(Value::from(r)), HashMap::new()).into())
13,514✔
743
        } else if let Ok(c) = ob.downcast::<PyComplex>() {
4,812,684✔
744
            let c: Complex64 = c.extract()?;
2,361,336✔
745
            if c.is_infinite() || c.is_nan() {
2,361,336✔
746
                return Err(ParameterError::InvalidValue.into());
×
747
            }
2,361,336✔
748
            Ok(ParameterExpression::new(SymbolExpr::Value(Value::from(c)), HashMap::new()).into())
2,361,336✔
749
        } else if let Ok(element) = ob.downcast::<PyParameterVectorElement>() {
2,451,348✔
750
            Ok(ParameterExpression::from_symbol(element.borrow().symbol.clone()).into())
50,858✔
751
        } else if let Ok(parameter) = ob.downcast::<PyParameter>() {
2,400,490✔
752
            Ok(ParameterExpression::from_symbol(parameter.borrow().symbol.clone()).into())
14,252✔
753
        } else {
754
            ob.extract::<PyParameterExpression>()
2,386,238✔
755
        }
756
    }
4,960,390✔
757

758
    pub fn coerce_into_py(&self, py: Python) -> PyResult<Py<PyAny>> {
32,362✔
759
        if let Ok(value) = self.inner.try_to_value(true) {
32,362✔
760
            match value {
14✔
761
                Value::Int(i) => Ok(PyInt::new(py, i).unbind().into_any()),
×
762
                Value::Real(r) => Ok(PyFloat::new(py, r).unbind().into_any()),
14✔
763
                Value::Complex(c) => Ok(PyComplex::from_complex_bound(py, c).unbind().into_any()),
×
764
            }
765
        } else if let Ok(symbol) = self.inner.try_to_symbol() {
32,348✔
766
            if symbol.index.is_some() {
22,754✔
767
                Ok(Py::new(py, PyParameterVectorElement::from_symbol(symbol))?.into_any())
5,964✔
768
            } else {
769
                Ok(Py::new(py, PyParameter::from_symbol(symbol))?.into_any())
16,790✔
770
            }
771
        } else {
772
            self.clone().into_py_any(py)
9,594✔
773
        }
774
    }
32,362✔
775
}
776

777
#[pymethods]
×
778
impl PyParameterExpression {
779
    /// This is a **strictly internal** constructor and **should not be used**.
780
    /// It is subject to arbitrary change in between Qiskit versions and cannot be relied on.
781
    /// Parameter expressions should always be constructed from applying operations on
782
    /// parameters, or by loading via QPY.
783
    ///
784
    /// The input values are allowed to be None for pickling purposes.
785
    #[new]
786
    #[pyo3(signature = (name_map=None, expr=None))]
787
    pub fn py_new(
70✔
788
        name_map: Option<HashMap<String, PyParameter>>,
70✔
789
        expr: Option<String>,
70✔
790
    ) -> PyResult<Self> {
70✔
791
        match (name_map, expr) {
70✔
792
            (None, None) => Ok(Self::default()),
×
793
            (Some(name_map), Some(expr)) => {
70✔
794
                // We first parse the expression and then update the symbols with the ones
795
                // the user provided. The replacement relies on the names to match.
796
                // This is hacky and we likely want a more reliably conversion from a SymPy object,
797
                // if we decide we want to continue supporting this.
798
                let expr = parse_expression(&expr)
70✔
799
                    .map_err(|_| PyRuntimeError::new_err("Failed parsing input expression"))?;
70✔
800
                let symbol_map: HashMap<String, Symbol> = name_map
70✔
801
                    .iter()
70✔
802
                    .map(|(string, param)| (string.clone(), param.symbol.clone()))
70✔
803
                    .collect();
70✔
804

805
                let replaced_expr = symbol_expr::replace_symbol(&expr, &symbol_map);
70✔
806

807
                let inner = ParameterExpression::new(replaced_expr, symbol_map);
70✔
808
                Ok(Self { inner })
70✔
809
            }
810
            _ => Err(PyValueError::new_err(
×
811
                "Pass either both a name_map and expr, or neither",
×
812
            )),
×
813
        }
814
    }
70✔
815

816
    #[allow(non_snake_case)]
817
    #[staticmethod]
818
    pub fn _Value(value: &Bound<PyAny>) -> PyResult<Self> {
34✔
819
        Self::extract_coerce(value)
34✔
820
    }
34✔
821

822
    /// Check if the expression corresponds to a plain symbol.
823
    ///
824
    /// Returns:
825
    ///     ``True`` is this expression corresponds to a symbol, ``False`` otherwise.
826
    pub fn is_symbol(&self) -> bool {
480✔
827
        matches!(self.inner.expr, SymbolExpr::Symbol(_))
480✔
828
    }
480✔
829

830
    /// Cast this expression to a numeric value.
831
    ///
832
    /// Args:
833
    ///     strict: If ``True`` (default) this function raises an error if there are any
834
    ///         unbound symbols in the expression. If ``False``, this allows casting
835
    ///         if the expression represents a numeric value, regardless of unbound symbols.
836
    ///         For example ``(0 * Parameter("x"))`` is 0 but has the symbol ``x`` present.
837
    #[pyo3(signature = (strict=true))]
838
    pub fn numeric(&self, py: Python, strict: bool) -> PyResult<Py<PyAny>> {
47,702✔
839
        match self.inner.try_to_value(strict)? {
47,702✔
840
            Value::Real(r) => r.into_py_any(py),
21,302✔
841
            Value::Int(i) => i.into_py_any(py),
11,792✔
842
            Value::Complex(c) => c.into_py_any(py),
14,488✔
843
        }
844
    }
47,702✔
845

846
    /// Return a SymPy equivalent of this expression.
847
    ///
848
    /// Returns:
849
    ///     A SymPy equivalent of this expression.
850
    pub fn sympify(&self, py: Python) -> PyResult<Py<PyAny>> {
480✔
851
        let py_sympify = SYMPIFY_PARAMETER_EXPRESSION.get(py);
480✔
852
        py_sympify.call1(py, (self.clone(),))
480✔
853
    }
480✔
854

855
    /// Get the parameters present in the expression.
856
    ///
857
    /// .. note::
858
    ///
859
    ///     Qiskit guarantees equality (via ``==``) of parameters retrieved from an expression
860
    ///     with the original :class:`.Parameter` objects used to create this expression,
861
    ///     but does **not guarantee** ``is`` comparisons to succeed.
862
    ///
863
    #[getter]
864
    pub fn parameters<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PySet>> {
135,912✔
865
        let py_parameters: Vec<Py<PyAny>> = self
135,912✔
866
            .inner
135,912✔
867
            .name_map
135,912✔
868
            .values()
135,912✔
869
            .map(|symbol| {
4,564,014✔
870
                match (&symbol.index, &symbol.vector) {
4,564,014✔
871
                    // if index and vector is set, it is an element
872
                    (Some(_index), Some(_vector)) => Ok(Py::new(
4,497,456✔
873
                        py,
4,497,456✔
874
                        PyParameterVectorElement::from_symbol(symbol.clone()),
4,497,456✔
875
                    )?
×
876
                    .into_any()),
4,497,456✔
877
                    // else, a normal parameter
878
                    _ => Ok(Py::new(py, PyParameter::from_symbol(symbol.clone()))?.into_any()),
66,558✔
879
                }
880
            })
4,564,014✔
881
            .collect::<PyResult<_>>()?;
135,912✔
882
        PySet::new(py, py_parameters)
135,912✔
883
    }
135,912✔
884

885
    /// Sine of the expression.
886
    #[inline]
887
    #[pyo3(name = "sin")]
888
    pub fn py_sin(&self) -> Self {
362✔
889
        self.inner.sin().into()
362✔
890
    }
362✔
891

892
    /// Cosine of the expression.
893
    #[inline]
894
    #[pyo3(name = "cos")]
895
    pub fn py_cos(&self) -> Self {
352✔
896
        self.inner.cos().into()
352✔
897
    }
352✔
898

899
    /// Tangent of the expression.
900
    #[inline]
901
    #[pyo3(name = "tan")]
902
    pub fn py_tan(&self) -> Self {
352✔
903
        self.inner.tan().into()
352✔
904
    }
352✔
905

906
    /// Arcsine of the expression.
907
    #[inline]
908
    pub fn arcsin(&self) -> Self {
100✔
909
        self.inner.asin().into()
100✔
910
    }
100✔
911

912
    /// Arccosine of the expression.
913
    #[inline]
914
    pub fn arccos(&self) -> Self {
100✔
915
        self.inner.acos().into()
100✔
916
    }
100✔
917

918
    /// Arctangent of the expression.
919
    #[inline]
920
    pub fn arctan(&self) -> Self {
100✔
921
        self.inner.atan().into()
100✔
922
    }
100✔
923

924
    /// Exponentiate the expression.
925
    #[inline]
926
    #[pyo3(name = "exp")]
927
    pub fn py_exp(&self) -> Self {
350✔
928
        self.inner.exp().into()
350✔
929
    }
350✔
930

931
    /// Take the natural logarithm of the expression.
932
    #[inline]
933
    #[pyo3(name = "log")]
934
    pub fn py_log(&self) -> Self {
266✔
935
        self.inner.log().into()
266✔
936
    }
266✔
937

938
    /// Take the absolute value of the expression.
939
    #[inline]
940
    #[pyo3(name = "abs")]
941
    pub fn py_abs(&self) -> Self {
12✔
942
        self.inner.abs().into()
12✔
943
    }
12✔
944

945
    /// Return the sign of the expression.
946
    #[inline]
947
    #[pyo3(name = "sign")]
948
    pub fn py_sign(&self) -> Self {
10✔
949
        self.inner.sign().into()
10✔
950
    }
10✔
951

952
    /// Return the complex conjugate of the expression.
953
    #[inline]
954
    #[pyo3(name = "conjugate")]
955
    pub fn py_conjugate(&self) -> Self {
1,832✔
956
        self.inner.conjugate().into()
1,832✔
957
    }
1,832✔
958

959
    /// Check whether the expression represents a real number.
960
    ///
961
    /// Note that this will return ``None`` if there are unbound parameters, in which case
962
    /// it cannot be determined whether the expression is real.
963
    #[inline]
964
    #[pyo3(name = "is_real")]
965
    pub fn py_is_real(&self) -> Option<bool> {
270✔
966
        self.inner.expr.is_real()
270✔
967
    }
270✔
968

969
    /// Return derivative of this expression with respect to the input parameter.
970
    ///
971
    /// Args:
972
    ///     param: The parameter with respect to which the derivative is calculated.
973
    ///
974
    /// Returns:
975
    ///     The derivative as either a constant numeric value or a symbolic
976
    ///     :class:`.ParameterExpression`.
977
    pub fn gradient(&self, param: &Bound<'_, PyAny>) -> PyResult<Py<PyAny>> {
74✔
978
        let symbol = symbol_from_py_parameter(param)?;
74✔
979
        let d_expr = self.inner.derivative(&symbol)?;
74✔
980

981
        // try converting to value and return as built-in numeric type
982
        match d_expr.try_to_value(false) {
70✔
983
            Ok(val) => match val {
20✔
984
                Value::Real(r) => r.into_py_any(param.py()),
18✔
985
                Value::Int(i) => i.into_py_any(param.py()),
2✔
986
                Value::Complex(c) => c.into_py_any(param.py()),
×
987
            },
988
            Err(_) => PyParameterExpression::from(d_expr).into_py_any(param.py()),
50✔
989
        }
990
    }
74✔
991

992
    /// Return all values in this equation.
993
    pub fn _values(&self, py: Python) -> PyResult<Vec<Py<PyAny>>> {
78✔
994
        self.inner
78✔
995
            .expr
78✔
996
            .values()
78✔
997
            .iter()
78✔
998
            .map(|val| match val {
78✔
999
                Value::Real(r) => r.into_py_any(py),
34✔
1000
                Value::Int(i) => i.into_py_any(py),
10✔
1001
                Value::Complex(c) => c.into_py_any(py),
×
1002
            })
44✔
1003
            .collect()
78✔
1004
    }
78✔
1005

1006
    /// Returns a new expression with replacement parameters.
1007
    ///
1008
    /// Args:
1009
    ///     parameter_map: Mapping from :class:`.Parameter`\ s in ``self`` to the
1010
    ///         :class:`.ParameterExpression` instances with which they should be replaced.
1011
    ///     allow_unknown_parameters: If ``False``, raises an error if ``parameter_map``
1012
    ///         contains :class:`.Parameter`\ s in the keys outside those present in the expression.
1013
    ///         If ``True``, any such parameters are simply ignored.
1014
    ///
1015
    /// Raises:
1016
    ///     CircuitError:
1017
    ///         - If parameter_map contains parameters outside those in self.
1018
    ///         - If the replacement parameters in ``parameter_map`` would result in
1019
    ///           a name conflict in the generated expression.
1020
    ///
1021
    /// Returns:
1022
    ///     A new expression with the specified parameters replaced.
1023
    #[pyo3(name = "subs")]
1024
    #[pyo3(signature = (parameter_map, allow_unknown_parameters=false))]
1025
    pub fn py_subs(
228✔
1026
        &self,
228✔
1027
        parameter_map: HashMap<PyParameter, Self>,
228✔
1028
        allow_unknown_parameters: bool,
228✔
1029
    ) -> PyResult<Self> {
228✔
1030
        // reduce the map to a HashMap<Symbol, ParameterExpression>
1031
        let map = parameter_map
228✔
1032
            .into_iter()
228✔
1033
            .map(|(param, expr)| Ok((param.symbol, expr.inner)))
232✔
1034
            .collect::<PyResult<_>>()?;
228✔
1035

1036
        // apply to the inner expression
1037
        match self.inner.subs(&map, allow_unknown_parameters) {
228✔
1038
            Ok(subbed) => Ok(subbed.into()),
224✔
1039
            Err(e) => Err(e.into()),
4✔
1040
        }
1041
    }
228✔
1042

1043
    /// Binds the provided set of parameters to their corresponding values.
1044
    ///
1045
    /// Args:
1046
    ///     parameter_values: Mapping of :class:`.Parameter` instances to the numeric value to which
1047
    ///         they will be bound.
1048
    ///     allow_unknown_parameters: If ``False``, raises an error if ``parameter_values``
1049
    ///         contains :class:`.Parameter`\ s in the keys outside those present in the expression.
1050
    ///         If ``True``, any such parameters are simply ignored.
1051
    ///
1052
    /// Raises:
1053
    ///     CircuitError:
1054
    ///         - If parameter_values contains parameters outside those in self.
1055
    ///         - If a non-numeric value is passed in ``parameter_values``.
1056
    ///     ZeroDivisionError:
1057
    ///         - If binding the provided values requires division by zero.
1058
    ///
1059
    /// Returns:
1060
    ///     A new expression parameterized by any parameters which were not bound by
1061
    ///     ``parameter_values``.
1062
    #[pyo3(name = "bind")]
1063
    #[pyo3(signature = (parameter_values, allow_unknown_parameters=false))]
1064
    pub fn py_bind(
127,040✔
1065
        &self,
127,040✔
1066
        parameter_values: HashMap<PyParameter, Bound<PyAny>>,
127,040✔
1067
        allow_unknown_parameters: bool,
127,040✔
1068
    ) -> PyResult<Self> {
127,040✔
1069
        // reduce the map to a HashMap<Symbol, Value>
1070
        let map = parameter_values
127,040✔
1071
            .iter()
127,040✔
1072
            .map(|(param, value)| {
4,555,014✔
1073
                let value = value.extract()?;
4,555,014✔
1074
                Ok((param.symbol(), value))
4,555,014✔
1075
            })
4,555,014✔
1076
            .collect::<PyResult<_>>()?;
127,040✔
1077

1078
        // apply to the inner expression
1079
        match self.inner.bind(&map, allow_unknown_parameters) {
127,040✔
1080
            Ok(bound) => Ok(bound.into()),
125,212✔
1081
            Err(e) => Err(e.into()),
1,828✔
1082
        }
1083
    }
127,040✔
1084

1085
    /// Bind all of the parameters in ``self`` to numeric values in the dictionary, returning a
1086
    /// numeric value.
1087
    ///
1088
    /// This is a special case of :meth:`bind` which can reach higher performance.  It is no problem
1089
    /// for the ``values`` dictionary to contain parameters that are not used in this expression;
1090
    /// the expectation is that the same bindings dictionary will be fed to other expressions as
1091
    /// well.
1092
    ///
1093
    /// It is an error to call this method with a ``values`` dictionary that does not bind all of
1094
    /// the values, or to call this method with non-numeric values, but this is not explicitly
1095
    /// checked, since this method is intended for performance-sensitive use.  Passing an incorrect
1096
    /// dictionary may result in unexpected behavior.
1097
    ///
1098
    /// Unlike :meth:`bind`, this method will not raise an exception if non-finite floating-point
1099
    /// values are encountered.
1100
    ///
1101
    /// Args:
1102
    ///     values: mapping of parameters to numeric values.
1103
    #[pyo3(name = "bind_all")]
1104
    #[pyo3(signature = (values, *))]
1105
    pub fn py_bind_all(&self, values: Bound<PyAny>) -> PyResult<Value> {
4✔
1106
        let mut partial_map = HashMap::with_capacity(self.inner.name_map.len());
4✔
1107
        for symbol in self.inner.name_map.values() {
8✔
1108
            let py_parameter = symbol.clone().into_pyobject(values.py())?;
8✔
1109
            partial_map.insert(symbol, values.get_item(py_parameter)?.extract()?);
8✔
1110
        }
1111
        let bound = self.inner.expr.bind(&partial_map);
2✔
1112
        bound.eval(true).ok_or_else(|| {
2✔
1113
            PyTypeError::new_err(format!(
×
1114
                "binding did not produce a numeric quantity: {bound:?}"
×
1115
            ))
1116
        })
×
1117
    }
4✔
1118

1119
    /// Assign one parameter to a value, which can either be numeric or another parameter
1120
    /// expression.
1121
    ///
1122
    /// Args:
1123
    ///     parameter: A parameter in this expression whose value will be updated.
1124
    ///     value: The new value to bind to.
1125
    ///
1126
    /// Returns:
1127
    ///     A new expression parameterized by any parameters which were not bound by assignment.
1128
    #[pyo3(name = "assign")]
1129
    pub fn py_assign(&self, parameter: PyParameter, value: &Bound<PyAny>) -> PyResult<Self> {
174✔
1130
        if let Ok(expr) = value.downcast::<Self>() {
174✔
1131
            let map = [(parameter, expr.borrow().clone())].into_iter().collect();
150✔
1132
            self.py_subs(map, false)
150✔
1133
        } else if value.extract::<Value>().is_ok() {
24✔
1134
            let map = [(parameter, value.clone())].into_iter().collect();
24✔
1135
            self.py_bind(map, false)
24✔
1136
        } else {
1137
            Err(PyValueError::new_err(
×
1138
                "Unexpected value in assign: {replacement:?}",
×
1139
            ))
×
1140
        }
1141
    }
174✔
1142

1143
    #[inline]
1144
    fn __copy__(slf: PyRef<Self>) -> PyRef<Self> {
×
1145
        // ParameterExpression is immutable.
1146
        slf
×
1147
    }
×
1148

1149
    #[inline]
1150
    fn __deepcopy__<'py>(slf: PyRef<'py, Self>, _memo: Bound<'py, PyAny>) -> PyRef<'py, Self> {
1,372✔
1151
        // Everything a ParameterExpression contains is immutable.
1152
        slf
1,372✔
1153
    }
1,372✔
1154

1155
    pub fn __eq__(&self, rhs: &Bound<PyAny>) -> PyResult<bool> {
33,396✔
1156
        if let Ok(rhs) = Self::extract_coerce(rhs) {
33,396✔
1157
            match rhs.inner.expr {
33,384✔
1158
                SymbolExpr::Value(v) => match self.inner.try_to_value(false) {
2,842✔
1159
                    Ok(e) => Ok(e == v),
2,642✔
1160
                    Err(_) => Ok(false),
200✔
1161
                },
1162
                _ => Ok(self.inner.expr == rhs.inner.expr),
30,542✔
1163
            }
1164
        } else {
1165
            Ok(false)
12✔
1166
        }
1167
    }
33,396✔
1168

1169
    #[inline]
1170
    pub fn __abs__(&self) -> Self {
362✔
1171
        self.inner.abs().into()
362✔
1172
    }
362✔
1173

1174
    #[inline]
1175
    pub fn __pos__(&self) -> Self {
4✔
1176
        self.clone()
4✔
1177
    }
4✔
1178

1179
    pub fn __neg__(&self) -> Self {
484✔
1180
        Self {
484✔
1181
            inner: ParameterExpression::new(-&self.inner.expr, self.inner.name_map.clone()),
484✔
1182
        }
484✔
1183
    }
484✔
1184

1185
    pub fn __add__(&self, rhs: &Bound<PyAny>) -> PyResult<Self> {
2,318,300✔
1186
        if let Ok(rhs) = Self::extract_coerce(rhs) {
2,318,300✔
1187
            Ok(self.inner.add(&rhs.inner)?.into())
2,318,288✔
1188
        } else {
1189
            Err(pyo3::exceptions::PyTypeError::new_err(
12✔
1190
                "Unsupported data type for __add__",
12✔
1191
            ))
12✔
1192
        }
1193
    }
2,318,300✔
1194

1195
    pub fn __radd__(&self, lhs: &Bound<PyAny>) -> PyResult<Self> {
64,000✔
1196
        if let Ok(lhs) = Self::extract_coerce(lhs) {
64,000✔
1197
            Ok(lhs.inner.add(&self.inner)?.into())
63,988✔
1198
        } else {
1199
            Err(pyo3::exceptions::PyTypeError::new_err(
12✔
1200
                "Unsupported data type for __radd__",
12✔
1201
            ))
12✔
1202
        }
1203
    }
64,000✔
1204

1205
    pub fn __sub__(&self, rhs: &Bound<PyAny>) -> PyResult<Self> {
18,462✔
1206
        if let Ok(rhs) = Self::extract_coerce(rhs) {
18,462✔
1207
            Ok(self.inner.sub(&rhs.inner)?.into())
18,450✔
1208
        } else {
1209
            Err(pyo3::exceptions::PyTypeError::new_err(
12✔
1210
                "Unsupported data type for __sub__",
12✔
1211
            ))
12✔
1212
        }
1213
    }
18,462✔
1214

1215
    pub fn __rsub__(&self, lhs: &Bound<PyAny>) -> PyResult<Self> {
4,366✔
1216
        if let Ok(lhs) = Self::extract_coerce(lhs) {
4,366✔
1217
            Ok(lhs.inner.sub(&self.inner)?.into())
4,354✔
1218
        } else {
1219
            Err(pyo3::exceptions::PyTypeError::new_err(
12✔
1220
                "Unsupported data type for __rsub__",
12✔
1221
            ))
12✔
1222
        }
1223
    }
4,366✔
1224

1225
    pub fn __mul__<'py>(&self, rhs: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
2,408,100✔
1226
        let py = rhs.py();
2,408,100✔
1227
        if let Ok(rhs) = Self::extract_coerce(rhs) {
2,408,100✔
1228
            match self.inner.mul(&rhs.inner) {
2,400,744✔
1229
                Ok(result) => PyParameterExpression::from(result).into_bound_py_any(py),
2,400,742✔
1230
                Err(e) => Err(PyErr::from(e)),
2✔
1231
            }
1232
        } else {
1233
            PyNotImplemented::get(py).into_bound_py_any(py)
7,356✔
1234
        }
1235
    }
2,408,100✔
1236

1237
    pub fn __rmul__(&self, lhs: &Bound<PyAny>) -> PyResult<Self> {
12,064✔
1238
        if let Ok(lhs) = Self::extract_coerce(lhs) {
12,064✔
1239
            Ok(lhs.inner.mul(&self.inner)?.into())
12,052✔
1240
        } else {
1241
            Err(pyo3::exceptions::PyTypeError::new_err(
12✔
1242
                "Unsupported data type for __rmul__",
12✔
1243
            ))
12✔
1244
        }
1245
    }
12,064✔
1246

1247
    pub fn __truediv__(&self, rhs: &Bound<PyAny>) -> PyResult<Self> {
8,530✔
1248
        if let Ok(rhs) = Self::extract_coerce(rhs) {
8,530✔
1249
            Ok(self.inner.div(&rhs.inner)?.into())
8,518✔
1250
        } else {
1251
            Err(pyo3::exceptions::PyTypeError::new_err(
12✔
1252
                "Unsupported data type for __truediv__",
12✔
1253
            ))
12✔
1254
        }
1255
    }
8,530✔
1256

1257
    pub fn __rtruediv__(&self, lhs: &Bound<PyAny>) -> PyResult<Self> {
4,070✔
1258
        if let Ok(lhs) = Self::extract_coerce(lhs) {
4,070✔
1259
            Ok(lhs.inner.div(&self.inner)?.into())
4,058✔
1260
        } else {
1261
            Err(pyo3::exceptions::PyTypeError::new_err(
12✔
1262
                "Unsupported data type for __rtruediv__",
12✔
1263
            ))
12✔
1264
        }
1265
    }
4,070✔
1266

1267
    pub fn __pow__(&self, rhs: &Bound<PyAny>, _modulo: Option<i32>) -> PyResult<Self> {
1,998✔
1268
        if let Ok(rhs) = Self::extract_coerce(rhs) {
1,998✔
1269
            Ok(self.inner.pow(&rhs.inner)?.into())
1,986✔
1270
        } else {
1271
            Err(pyo3::exceptions::PyTypeError::new_err(
12✔
1272
                "Unsupported data type for __pow__",
12✔
1273
            ))
12✔
1274
        }
1275
    }
1,998✔
1276

1277
    pub fn __rpow__(&self, lhs: &Bound<PyAny>, _modulo: Option<i32>) -> PyResult<Self> {
1,042✔
1278
        if let Ok(lhs) = Self::extract_coerce(lhs) {
1,042✔
1279
            Ok(lhs.inner.pow(&self.inner)?.into())
1,030✔
1280
        } else {
1281
            Err(pyo3::exceptions::PyTypeError::new_err(
12✔
1282
                "Unsupported data type for __rpow__",
12✔
1283
            ))
12✔
1284
        }
1285
    }
1,042✔
1286

1287
    pub fn __int__(&self) -> PyResult<i64> {
176✔
1288
        match self.inner.try_to_value(false)? {
176✔
1289
            Value::Complex(_) => Err(PyTypeError::new_err(
42✔
1290
                "Cannot cast complex parameter to int.",
42✔
1291
            )),
42✔
1292
            Value::Real(r) => {
90✔
1293
                let rounded = r.floor();
90✔
1294
                Ok(rounded as i64)
90✔
1295
            }
1296
            Value::Int(i) => Ok(i),
42✔
1297
        }
1298
    }
176✔
1299

1300
    pub fn __float__(&self) -> PyResult<f64> {
672✔
1301
        match self.inner.try_to_value(false)? {
672✔
1302
            Value::Complex(c) => {
44✔
1303
                if c.im.abs() > SYMEXPR_EPSILON {
44✔
1304
                    Err(PyTypeError::new_err(
44✔
1305
                        "Could not cast complex parameter expression to float.",
44✔
1306
                    ))
44✔
1307
                } else {
1308
                    Ok(c.re)
×
1309
                }
1310
            }
1311
            Value::Real(r) => Ok(r),
114✔
1312
            Value::Int(i) => Ok(i as f64),
56✔
1313
        }
1314
    }
672✔
1315

1316
    pub fn __complex__(&self) -> PyResult<Complex64> {
77,856✔
1317
        match self.inner.try_to_value(false)? {
77,856✔
1318
            Value::Complex(c) => Ok(c),
26,888✔
1319
            Value::Real(r) => Ok(Complex64::new(r, 0.)),
34,984✔
1320
            Value::Int(i) => Ok(Complex64::new(i as f64, 0.)),
15,982✔
1321
        }
1322
    }
77,856✔
1323

1324
    pub fn __str__(&self) -> String {
336,816✔
1325
        self.to_string()
336,816✔
1326
    }
336,816✔
1327

1328
    pub fn __hash__(&self, py: Python) -> PyResult<u64> {
9,164,748✔
1329
        match self.inner.try_to_value(false) {
9,164,748✔
1330
            // if a value, we promise to match the hash of the raw value!
1331
            Ok(value) => {
2✔
1332
                let py_hash = BUILTIN_HASH.get_bound(py);
2✔
1333
                match value {
2✔
1334
                    Value::Complex(c) => py_hash.call1((c,))?.extract::<u64>(),
×
1335
                    Value::Real(r) => py_hash.call1((r,))?.extract::<u64>(),
2✔
1336
                    Value::Int(i) => py_hash.call1((i,))?.extract::<u64>(),
×
1337
                }
1338
            }
1339
            Err(_) => {
1340
                let mut hasher = DefaultHasher::new();
9,164,746✔
1341
                self.inner.expr.string_id().hash(&mut hasher);
9,164,746✔
1342
                Ok(hasher.finish())
9,164,746✔
1343
            }
1344
        }
1345
    }
9,164,748✔
1346

1347
    fn __getstate__(&self) -> PyResult<(Vec<OPReplay>, Option<ParameterValueType>)> {
7,884✔
1348
        // We distinguish in two cases:
1349
        //  (a) This is indeed an expression which can be rebuild from the QPY replay. This means
1350
        //      the replay is *not empty* and it contains all symbols.
1351
        //  (b) This expression is in fact only a Value or a Symbol. In this case, the QPY replay
1352
        //      will be empty and we instead pass a `ParameterValueType` for reconstruction.
1353
        let qpy = self._qpy_replay()?;
7,884✔
1354
        if !qpy.is_empty() {
7,884✔
1355
            Ok((qpy, None))
7,884✔
1356
        } else {
1357
            let value = ParameterValueType::extract_from_expr(&self.inner.expr);
×
1358
            if value.is_none() {
×
1359
                Err(PyValueError::new_err(format!(
×
1360
                    "Failed to serialize the parameter expression: {self:?}"
×
1361
                )))
×
1362
            } else {
1363
                Ok((qpy, value))
×
1364
            }
1365
        }
1366
    }
7,884✔
1367

1368
    fn __setstate__(&mut self, state: (Vec<OPReplay>, Option<ParameterValueType>)) -> PyResult<()> {
×
1369
        // if there a replay, load from the replay
1370
        if !state.0.is_empty() {
×
1371
            let from_qpy = ParameterExpression::from_qpy(&state.0)?;
×
1372
            self.inner = from_qpy;
×
1373
        // otherwise, load from the ParameterValueType
1374
        } else if let Some(value) = state.1 {
×
1375
            let expr = ParameterExpression::from(value);
×
1376
            self.inner = expr;
×
1377
        } else {
×
1378
            return Err(PyValueError::new_err(
×
1379
                "Failed to read QPY replay or extract value.",
×
1380
            ));
×
1381
        }
1382
        Ok(())
×
1383
    }
×
1384

1385
    #[getter]
1386
    fn _qpy_replay(&self) -> PyResult<Vec<OPReplay>> {
8,186✔
1387
        let mut replay = Vec::new();
8,186✔
1388
        qpy_replay(&self.inner, &self.inner.name_map, &mut replay);
8,186✔
1389
        Ok(replay)
8,186✔
1390
    }
8,186✔
1391
}
1392

1393
/// A compile-time symbolic parameter.
1394
///
1395
/// The value of a :class:`.Parameter` must be entirely determined before a circuit begins execution.
1396
/// Typically this will mean that you should supply values for all :class:`.Parameter`\ s in a
1397
/// circuit using :meth:`.QuantumCircuit.assign_parameters`, though certain hardware vendors may
1398
/// allow you to give them a circuit in terms of these parameters, provided you also pass the values
1399
/// separately.
1400
///
1401
/// This is the atom of :class:`.ParameterExpression`, and is itself an expression.  The numeric
1402
/// value of a parameter need not be fixed while the circuit is being defined.
1403
///
1404
/// Examples:
1405
///
1406
///     Construct a variable-rotation X gate using circuit parameters.
1407
///
1408
///     .. plot::
1409
///         :alt: Circuit diagram output by the previous code.
1410
///         :include-source:
1411
///
1412
///         from qiskit.circuit import QuantumCircuit, Parameter
1413
///
1414
///         # create the parameter
1415
///         phi = Parameter("phi")
1416
///         qc = QuantumCircuit(1)
1417
///
1418
///         # parameterize the rotation
1419
///         qc.rx(phi, 0)
1420
///         qc.draw("mpl")
1421
///
1422
///         # bind the parameters after circuit to create a bound circuit
1423
///         bc = qc.assign_parameters({phi: 3.14})
1424
///         bc.measure_all()
1425
///         bc.draw("mpl")
1426
#[pyclass(sequence, subclass, module="qiskit._accelerate.circuit", extends=PyParameterExpression, name="Parameter")]
1427
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
1428
pub struct PyParameter {
1429
    symbol: Symbol,
1430
}
1431

1432
impl Hash for PyParameter {
1433
    fn hash<H: Hasher>(&self, state: &mut H) {
4,556,372✔
1434
        self.symbol.hash(state);
4,556,372✔
1435
    }
4,556,372✔
1436
}
1437

1438
// This needs to be implemented manually, since PyO3 does not provide this conversion
1439
// for subclasses.
1440
impl<'py> IntoPyObject<'py> for PyParameter {
1441
    type Target = PyParameter;
1442
    type Output = Bound<'py, Self::Target>;
1443
    type Error = PyErr;
1444

1445
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
9,136✔
1446
        let symbol = &self.symbol;
9,136✔
1447
        let symbol_expr = SymbolExpr::Symbol(Arc::new(symbol.clone()));
9,136✔
1448
        let expr = ParameterExpression::from_symbol_expr(symbol_expr);
9,136✔
1449
        let py_expr = PyParameterExpression::from(expr);
9,136✔
1450

1451
        Ok(Py::new(py, (self, py_expr))?.into_bound(py))
9,136✔
1452
    }
9,136✔
1453
}
1454

1455
impl PyParameter {
1456
    /// Get a Python class initialization from a symbol.
1457
    pub fn from_symbol(symbol: Symbol) -> PyClassInitializer<Self> {
4,672,850✔
1458
        let expr = SymbolExpr::Symbol(Arc::new(symbol.clone()));
4,672,850✔
1459

1460
        let py_parameter = Self { symbol };
4,672,850✔
1461
        let py_expr: PyParameterExpression = ParameterExpression::from_symbol_expr(expr).into();
4,672,850✔
1462

1463
        PyClassInitializer::from(py_expr).add_subclass(py_parameter)
4,672,850✔
1464
    }
4,672,850✔
1465

1466
    /// Get a reference to the underlying symbol.
1467
    pub fn symbol(&self) -> &Symbol {
4,557,318✔
1468
        &self.symbol
4,557,318✔
1469
    }
4,557,318✔
1470
}
1471

1472
#[pymethods]
×
1473
impl PyParameter {
1474
    /// Args:
1475
    ///     name: name of the parameter, used for visual representation. This can
1476
    ///         be any Unicode string, e.g. ``"Ï•"``.
1477
    ///     uuid: For advanced usage only.  Override the UUID of this parameter, in order to make it
1478
    ///         compare equal to some other parameter object.  By default, two parameters with the
1479
    ///         same name do not compare equal to help catch shadowing bugs when two circuits
1480
    ///         containing the same named parameters are spurious combined.  Setting the ``uuid``
1481
    ///         field when creating two parameters to the same thing (along with the same name)
1482
    ///         allows them to be equal.  This is useful during serialization and deserialization.
1483
    #[new]
1484
    #[pyo3(signature = (name, uuid=None))]
1485
    fn py_new(
83,086✔
1486
        py: Python<'_>,
83,086✔
1487
        name: String,
83,086✔
1488
        uuid: Option<Py<PyAny>>,
83,086✔
1489
    ) -> PyResult<PyClassInitializer<Self>> {
83,086✔
1490
        let uuid = uuid_from_py(py, uuid)?;
83,086✔
1491
        let symbol = Symbol::new(name.as_str(), uuid, None);
83,086✔
1492
        let expr = SymbolExpr::Symbol(Arc::new(symbol.clone()));
83,086✔
1493

1494
        let py_parameter = Self { symbol };
83,086✔
1495
        let py_expr: PyParameterExpression = ParameterExpression::from_symbol_expr(expr).into();
83,086✔
1496

1497
        Ok(PyClassInitializer::from(py_expr).add_subclass(py_parameter))
83,086✔
1498
    }
83,086✔
1499

1500
    /// Returns the name of the :class:`.Parameter`.
1501
    #[getter]
1502
    fn name<'py>(&self, py: Python<'py>) -> Bound<'py, PyString> {
21,344✔
1503
        PyString::new(py, &self.symbol.repr(false))
21,344✔
1504
    }
21,344✔
1505

1506
    /// Returns the :class:`~uuid.UUID` of the :class:`Parameter`.
1507
    ///
1508
    /// In advanced use cases, this property can be passed to the
1509
    /// :class:`.Parameter` constructor to produce an instance that compares
1510
    /// equal to another instance.
1511
    #[getter]
1512
    fn uuid(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
1,368✔
1513
        uuid_to_py(py, self.symbol.uuid)
1,368✔
1514
    }
1,368✔
1515

1516
    pub fn __repr__<'py>(&self, py: Python<'py>) -> Bound<'py, PyString> {
64✔
1517
        let str = format!("Parameter({})", self.symbol.repr(false),);
64✔
1518
        PyString::new(py, str.as_str())
64✔
1519
    }
64✔
1520

1521
    pub fn __getnewargs__(&self) -> (String, u128) {
24,144✔
1522
        (self.symbol.repr(false), self.symbol.uuid.as_u128())
24,144✔
1523
    }
24,144✔
1524

1525
    pub fn __getstate__(&self) -> (String, u128) {
24,144✔
1526
        (self.symbol.repr(false), self.symbol.uuid.as_u128())
24,144✔
1527
    }
24,144✔
1528

1529
    pub fn __setstate__(&mut self, state: (String, u128)) {
16✔
1530
        let name = state.0.as_str();
16✔
1531
        let uuid = Uuid::from_u128(state.1);
16✔
1532
        let symbol = Symbol::new(name, Some(uuid), None);
16✔
1533
        self.symbol = symbol;
16✔
1534
    }
16✔
1535

1536
    fn __copy__(slf: PyRef<Self>) -> PyRef<Self> {
×
1537
        // Parameter is immutable. Note that this **cannot** be deferred to the parent class
1538
        // since PyO3 would then always return the parent type.
1539
        slf
×
1540
    }
×
1541

1542
    fn __deepcopy__<'py>(slf: PyRef<'py, Self>, _memo: Bound<'py, PyAny>) -> PyRef<'py, Self> {
147,556✔
1543
        // Everything inside a Parameter is immutable. Note that this **cannot** be deferred to the
1544
        // parent class since PyO3 would then always return the parent type.
1545
        slf
147,556✔
1546
    }
147,556✔
1547

1548
    #[pyo3(name = "subs")]
1549
    #[pyo3(signature = (parameter_map, allow_unknown_parameters=false))]
1550
    pub fn py_subs<'py>(
416✔
1551
        &self,
416✔
1552
        py: Python<'py>,
416✔
1553
        parameter_map: HashMap<PyParameter, Bound<'py, PyAny>>,
416✔
1554
        allow_unknown_parameters: bool,
416✔
1555
    ) -> PyResult<Bound<'py, PyAny>> {
416✔
1556
        // We implement this method on this class, and do not defer to the parent, such
1557
        // that x.subs({x: y}) remains a Parameter, and is not upgraded to an expression.
1558
        // Also this should be faster than going via ParameterExpression, which constructs
1559
        // intermediary HashMaps we don't need here.
1560
        match parameter_map.get(self) {
416✔
1561
            None => {
1562
                if allow_unknown_parameters {
4✔
1563
                    self.clone().into_bound_py_any(py)
2✔
1564
                } else {
1565
                    Err(CircuitError::new_err(
2✔
1566
                        "Cannot bind parameters not present in parameter.",
2✔
1567
                    ))
2✔
1568
                }
1569
            }
1570
            Some(replacement) => {
412✔
1571
                if allow_unknown_parameters || parameter_map.len() == 1 {
412✔
1572
                    Ok(replacement.clone())
412✔
1573
                } else {
1574
                    Err(CircuitError::new_err(
×
1575
                        "Cannot bind parameters not present in parameter.",
×
1576
                    ))
×
1577
                }
1578
            }
1579
        }
1580
    }
416✔
1581

1582
    #[pyo3(name = "bind")]
1583
    #[pyo3(signature = (parameter_values, allow_unknown_parameters=false))]
1584
    pub fn py_bind<'py>(
146✔
1585
        &self,
146✔
1586
        py: Python<'py>,
146✔
1587
        parameter_values: HashMap<PyParameter, Bound<'py, PyAny>>,
146✔
1588
        allow_unknown_parameters: bool,
146✔
1589
    ) -> PyResult<Bound<'py, PyAny>> {
146✔
1590
        // Returns PyAny to cover Parameter and ParameterExpression(value).
1591
        match parameter_values.get(self) {
146✔
1592
            None => {
1593
                if allow_unknown_parameters {
×
1594
                    self.clone().into_bound_py_any(py)
×
1595
                } else {
1596
                    Err(CircuitError::new_err(
×
1597
                        "Cannot bind parameters not present in parameter.",
×
1598
                    ))
×
1599
                }
1600
            }
1601
            Some(replacement) => {
146✔
1602
                if allow_unknown_parameters || parameter_values.len() == 1 {
146✔
1603
                    let expr = PyParameterExpression::extract_coerce(replacement)?;
146✔
1604
                    if let SymbolExpr::Value(_) = &expr.inner.expr {
146✔
1605
                        expr.clone().into_bound_py_any(py)
146✔
1606
                    } else {
1607
                        Err(PyValueError::new_err("Invalid binding value."))
×
1608
                    }
1609
                } else {
1610
                    Err(CircuitError::new_err(
×
1611
                        "Cannot bind parameters not present in parameter.",
×
1612
                    ))
×
1613
                }
1614
            }
1615
        }
1616
    }
146✔
1617

1618
    #[pyo3(name = "bind_all")]
1619
    #[pyo3(signature = (values, *))]
1620
    pub fn py_bind_all<'py>(
4✔
1621
        slf_: Bound<'py, Self>,
4✔
1622
        values: Bound<'py, PyAny>,
4✔
1623
    ) -> PyResult<Bound<'py, PyAny>> {
4✔
1624
        values.get_item(slf_)
4✔
1625
    }
4✔
1626

1627
    #[pyo3(name = "assign")]
1628
    pub fn py_assign<'py>(
408✔
1629
        &self,
408✔
1630
        py: Python<'py>,
408✔
1631
        parameter: PyParameter,
408✔
1632
        value: &Bound<'py, PyAny>,
408✔
1633
    ) -> PyResult<Bound<'py, PyAny>> {
408✔
1634
        if value.downcast::<PyParameterExpression>().is_ok() {
408✔
1635
            let map = [(parameter, value.clone())].into_iter().collect();
408✔
1636
            self.py_subs(py, map, false)
408✔
1637
        } else if value.extract::<Value>().is_ok() {
×
1638
            let map = [(parameter, value.clone())].into_iter().collect();
×
1639
            self.py_bind(py, map, false)
×
1640
        } else {
1641
            Err(PyValueError::new_err(
×
1642
                "Unexpected value in assign: {replacement:?}",
×
1643
            ))
×
1644
        }
1645
    }
408✔
1646
}
1647

1648
/// An element of a :class:`.ParameterVector`.
1649
///
1650
/// .. note::
1651
///     There is very little reason to ever construct this class directly.  Objects of this type are
1652
///     automatically constructed efficiently as part of creating a :class:`.ParameterVector`.
1653
#[pyclass(sequence, subclass, module="qiskit._accelerate.circuit", extends=PyParameter, name="ParameterVectorElement")]
1654
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd)]
1655
pub struct PyParameterVectorElement {
1656
    symbol: Symbol,
1657
}
1658

1659
impl<'py> IntoPyObject<'py> for PyParameterVectorElement {
1660
    type Target = PyParameterVectorElement;
1661
    type Output = Bound<'py, Self::Target>;
1662
    type Error = PyErr;
1663

1664
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
176✔
1665
        let symbol = &self.symbol;
176✔
1666
        let py_param = PyParameter::from_symbol(symbol.clone());
176✔
1667
        let py_element = py_param.add_subclass(self);
176✔
1668

1669
        Ok(Py::new(py, py_element)?.into_bound(py))
176✔
1670
    }
176✔
1671
}
1672

1673
impl PyParameterVectorElement {
1674
    pub fn symbol(&self) -> &Symbol {
16,460✔
1675
        &self.symbol
16,460✔
1676
    }
16,460✔
1677

1678
    pub fn from_symbol(symbol: Symbol) -> PyClassInitializer<Self> {
4,556,092✔
1679
        let py_element = Self {
4,556,092✔
1680
            symbol: symbol.clone(),
4,556,092✔
1681
        };
4,556,092✔
1682
        let py_parameter = PyParameter::from_symbol(symbol);
4,556,092✔
1683

1684
        py_parameter.add_subclass(py_element)
4,556,092✔
1685
    }
4,556,092✔
1686
}
1687

1688
#[pymethods]
×
1689
impl PyParameterVectorElement {
1690
    #[new]
1691
    #[pyo3(signature = (vector, index, uuid=None))]
1692
    pub fn py_new(
27,860✔
1693
        py: Python<'_>,
27,860✔
1694
        vector: Py<PyAny>,
27,860✔
1695
        index: u32,
27,860✔
1696
        uuid: Option<Py<PyAny>>,
27,860✔
1697
    ) -> PyResult<PyClassInitializer<Self>> {
27,860✔
1698
        let vector_name = vector.getattr(py, "name")?.extract::<String>(py)?;
27,860✔
1699
        let uuid = uuid_from_py(py, uuid)?.unwrap_or(Uuid::new_v4());
27,860✔
1700

1701
        let symbol = Symbol::py_new(
27,860✔
1702
            &vector_name,
27,860✔
1703
            Some(uuid.as_u128()),
27,860✔
1704
            Some(index),
27,860✔
1705
            Some(vector.clone_ref(py)),
27,860✔
1706
        )?;
×
1707

1708
        let py_parameter = PyParameter::from_symbol(symbol.clone());
27,860✔
1709
        let py_element = Self { symbol };
27,860✔
1710

1711
        Ok(py_parameter.add_subclass(py_element))
27,860✔
1712
    }
27,860✔
1713

1714
    pub fn __getnewargs__(&self, py: Python) -> PyResult<(Py<PyAny>, u32, Option<Py<PyAny>>)> {
8✔
1715
        let vector = self
8✔
1716
            .symbol
8✔
1717
            .vector
8✔
1718
            .clone()
8✔
1719
            .expect("vector element should have a vector");
8✔
1720
        let index = self
8✔
1721
            .symbol
8✔
1722
            .index
8✔
1723
            .expect("vector element should have an index");
8✔
1724
        let uuid = uuid_to_py(py, self.symbol.uuid)?;
8✔
1725
        Ok((vector, index, Some(uuid)))
8✔
1726
    }
8✔
1727

1728
    pub fn __repr__<'py>(&self, py: Python<'py>) -> Bound<'py, PyString> {
48✔
1729
        let str = format!("ParameterVectorElement({})", self.symbol.repr(false),);
48✔
1730
        PyString::new(py, str.as_str())
48✔
1731
    }
48✔
1732

1733
    pub fn __getstate__(&self, py: Python) -> PyResult<(Py<PyAny>, u32, Option<Py<PyAny>>)> {
8✔
1734
        self.__getnewargs__(py)
8✔
1735
    }
8✔
1736

1737
    pub fn __setstate__(
×
1738
        &mut self,
×
1739
        py: Python,
×
1740
        state: (Py<PyAny>, u32, Option<Py<PyAny>>),
×
1741
    ) -> PyResult<()> {
×
1742
        let vector = state.0;
×
1743
        let index = state.1;
×
1744
        let vector_name = vector.getattr(py, "name")?.extract::<String>(py)?;
×
1745
        let uuid = uuid_from_py(py, state.2)?.map(|id| id.as_u128());
×
1746
        self.symbol = Symbol::py_new(&vector_name, uuid, Some(index), Some(vector))?;
×
1747
        Ok(())
×
1748
    }
×
1749

1750
    /// Get the index of this element in the parent vector.
1751
    #[getter]
1752
    pub fn index(&self) -> u32 {
336✔
1753
        self.symbol
336✔
1754
            .index
336✔
1755
            .expect("A vector element should have an index")
336✔
1756
    }
336✔
1757

1758
    /// Get the parent vector instance.
1759
    #[getter]
1760
    pub fn vector(&self) -> Py<PyAny> {
668✔
1761
        self.symbol
668✔
1762
            .clone()
668✔
1763
            .vector
668✔
1764
            .expect("A vector element should have a vector")
668✔
1765
    }
668✔
1766

1767
    /// For backward compatibility only. This should not be used and we ought to update those
1768
    /// usages!
1769
    #[getter]
1770
    pub fn _vector(&self) -> Py<PyAny> {
×
1771
        self.vector()
×
1772
    }
×
1773

1774
    fn __copy__(slf: PyRef<Self>) -> PyRef<Self> {
×
1775
        // ParameterVectorElement is immutable.
1776
        slf
×
1777
    }
×
1778

1779
    fn __deepcopy__<'py>(slf: PyRef<'py, Self>, _memo: Bound<'py, PyAny>) -> PyRef<'py, Self> {
10,988✔
1780
        // Everything a ParameterVectorElement contains is immutable.
1781
        slf
10,988✔
1782
    }
10,988✔
1783
}
1784

1785
/// Try to extract a Uuid from a Python object, which could be a Python UUID or int.
1786
fn uuid_from_py(py: Python<'_>, uuid: Option<Py<PyAny>>) -> PyResult<Option<Uuid>> {
110,946✔
1787
    if let Some(val) = uuid {
110,946✔
1788
        // construct from u128
1789
        let as_u128 = if let Ok(as_u128) = val.extract::<u128>(py) {
28,042✔
1790
            as_u128
16✔
1791
        // construct from Python UUID type
1792
        } else if val.bind(py).is_exact_instance(UUID.get_bound(py)) {
28,026✔
1793
            val.getattr(py, "int")?.extract::<u128>(py)?
28,026✔
1794
        // invalid format
1795
        } else {
1796
            return Err(PyTypeError::new_err("not a UUID!"));
×
1797
        };
1798
        Ok(Some(Uuid::from_u128(as_u128)))
28,042✔
1799
    } else {
1800
        Ok(None)
82,904✔
1801
    }
1802
}
110,946✔
1803

1804
/// Convert a Rust Uuid object to a Python UUID object.
1805
fn uuid_to_py(py: Python<'_>, uuid: Uuid) -> PyResult<Py<PyAny>> {
1,376✔
1806
    let uuid = uuid.as_u128();
1,376✔
1807
    let kwargs = [("int", uuid)].into_py_dict(py)?;
1,376✔
1808
    Ok(UUID.get_bound(py).call((), Some(&kwargs))?.unbind())
1,376✔
1809
}
1,376✔
1810

1811
/// Extract a [Symbol] for a Python object, which could either be a Parameter or a
1812
/// ParameterVectorElement.
1813
fn symbol_from_py_parameter(param: &Bound<'_, PyAny>) -> PyResult<Symbol> {
74✔
1814
    if let Ok(element) = param.extract::<PyParameterVectorElement>() {
74✔
1815
        Ok(element.symbol.clone())
×
1816
    } else if let Ok(parameter) = param.extract::<PyParameter>() {
74✔
1817
        Ok(parameter.symbol.clone())
74✔
1818
    } else {
1819
        Err(PyValueError::new_err("Could not extract parameter"))
×
1820
    }
1821
}
74✔
1822

1823
/// A singular parameter value used for QPY serialization. This covers anything
1824
/// but a [PyParameterExpression], which is represented by [None] in the serialization.
1825
#[derive(IntoPyObject, FromPyObject, Clone, Debug)]
1826
pub enum ParameterValueType {
1827
    Int(i64),
1828
    Float(f64),
1829
    Complex(Complex64),
1830
    Parameter(PyParameter),
1831
    VectorElement(PyParameterVectorElement),
1832
}
1833

1834
impl ParameterValueType {
1835
    fn extract_from_expr(expr: &SymbolExpr) -> Option<ParameterValueType> {
18,190✔
1836
        if let Some(value) = expr.eval(true) {
18,190✔
1837
            match value {
7,472✔
1838
                Value::Int(i) => Some(ParameterValueType::Int(i)),
100✔
1839
                Value::Real(r) => Some(ParameterValueType::Float(r)),
7,364✔
1840
                Value::Complex(c) => Some(ParameterValueType::Complex(c)),
8✔
1841
            }
1842
        } else if let SymbolExpr::Symbol(symbol) = expr {
10,718✔
1843
            match symbol.index {
9,310✔
1844
                None => {
1845
                    let param = PyParameter {
9,134✔
1846
                        symbol: symbol.as_ref().clone(),
9,134✔
1847
                    };
9,134✔
1848
                    Some(ParameterValueType::Parameter(param))
9,134✔
1849
                }
1850
                Some(_) => {
1851
                    let param = PyParameterVectorElement {
176✔
1852
                        symbol: symbol.as_ref().clone(),
176✔
1853
                    };
176✔
1854
                    Some(ParameterValueType::VectorElement(param))
176✔
1855
                }
1856
            }
1857
        } else {
1858
            // ParameterExpressions have the value None, as they must be constructed
1859
            None
1,408✔
1860
        }
1861
    }
18,190✔
1862
}
1863

1864
impl From<ParameterValueType> for ParameterExpression {
1865
    fn from(value: ParameterValueType) -> Self {
×
1866
        match value {
×
1867
            ParameterValueType::Parameter(param) => {
×
1868
                let expr = SymbolExpr::Symbol(Arc::new(param.symbol));
×
1869
                Self::from_symbol_expr(expr)
×
1870
            }
1871
            ParameterValueType::VectorElement(param) => {
×
1872
                let expr = SymbolExpr::Symbol(Arc::new(param.symbol));
×
1873
                Self::from_symbol_expr(expr)
×
1874
            }
1875
            ParameterValueType::Int(i) => {
×
1876
                let expr = SymbolExpr::Value(Value::Int(i));
×
1877
                Self::from_symbol_expr(expr)
×
1878
            }
1879
            ParameterValueType::Float(f) => {
×
1880
                let expr = SymbolExpr::Value(Value::Real(f));
×
1881
                Self::from_symbol_expr(expr)
×
1882
            }
1883
            ParameterValueType::Complex(c) => {
×
1884
                let expr = SymbolExpr::Value(Value::Complex(c));
×
1885
                Self::from_symbol_expr(expr)
×
1886
            }
1887
        }
1888
    }
×
1889
}
1890

1891
#[pyclass(module = "qiskit._accelerate.circuit")]
×
1892
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
1893
#[repr(u8)]
1894
pub enum OpCode {
1895
    ADD = 0,
1896
    SUB = 1,
1897
    MUL = 2,
1898
    DIV = 3,
1899
    POW = 4,
1900
    SIN = 5,
1901
    COS = 6,
1902
    TAN = 7,
1903
    ASIN = 8,
1904
    ACOS = 9,
1905
    EXP = 10,
1906
    LOG = 11,
1907
    SIGN = 12,
1908
    GRAD = 13, // for backward compatibility, unused in Rust's ParameterExpression
1909
    CONJ = 14,
1910
    SUBSTITUTE = 15, // for backward compatibility, unused in Rust's ParameterExpression
1911
    ABS = 16,
1912
    ATAN = 17,
1913
    RSUB = 18,
1914
    RDIV = 19,
1915
    RPOW = 20,
1916
}
1917

1918
impl From<OpCode> for u8 {
1919
    fn from(value: OpCode) -> Self {
×
1920
        value as u8
×
1921
    }
×
1922
}
1923

1924
unsafe impl ::bytemuck::CheckedBitPattern for OpCode {
1925
    type Bits = u8;
1926

1927
    #[inline(always)]
1928
    fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
×
1929
        *bits <= 20
×
1930
    }
×
1931
}
1932

1933
unsafe impl ::bytemuck::NoUninit for OpCode {}
1934

1935
#[pymethods]
×
1936
impl OpCode {
1937
    #[new]
1938
    fn py_new(value: u8) -> PyResult<Self> {
×
1939
        let code: OpCode = ::bytemuck::checked::try_cast(value)
×
1940
            .map_err(|_| ParameterError::InvalidU8ToOpCode(value))?;
×
1941
        Ok(code)
×
1942
    }
×
1943

1944
    fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
560✔
1945
        if let Ok(code) = other.downcast::<OpCode>() {
560✔
1946
            *code.borrow() == *self
560✔
1947
        } else {
1948
            false
×
1949
        }
1950
    }
560✔
1951

1952
    fn __hash__(&self) -> u8 {
2,088✔
1953
        *self as u8
2,088✔
1954
    }
2,088✔
1955

1956
    fn __getnewargs__(&self) -> (u8,) {
8,964✔
1957
        (*self as u8,)
8,964✔
1958
    }
8,964✔
1959
}
1960

1961
// enum for QPY replay
1962
#[pyclass(sequence, module = "qiskit._accelerate.circuit")]
1963
#[derive(Clone, Debug)]
1964
pub struct OPReplay {
1965
    pub op: OpCode,
1966
    pub lhs: Option<ParameterValueType>,
1967
    pub rhs: Option<ParameterValueType>,
1968
}
1969

1970
#[pymethods]
×
1971
impl OPReplay {
1972
    #[new]
1973
    pub fn py_new(
×
1974
        op: OpCode,
×
1975
        lhs: Option<ParameterValueType>,
×
1976
        rhs: Option<ParameterValueType>,
×
1977
    ) -> OPReplay {
×
1978
        OPReplay { op, lhs, rhs }
×
1979
    }
×
1980

1981
    #[getter]
1982
    fn op(&self) -> OpCode {
1,194✔
1983
        self.op
1,194✔
1984
    }
1,194✔
1985

1986
    #[getter]
1987
    fn lhs(&self) -> Option<ParameterValueType> {
630✔
1988
        self.lhs.clone()
630✔
1989
    }
630✔
1990

1991
    #[getter]
1992
    fn rhs(&self) -> Option<ParameterValueType> {
630✔
1993
        self.rhs.clone()
630✔
1994
    }
630✔
1995

1996
    fn __getnewargs__(
8,964✔
1997
        &self,
8,964✔
1998
    ) -> (
8,964✔
1999
        OpCode,
8,964✔
2000
        Option<ParameterValueType>,
8,964✔
2001
        Option<ParameterValueType>,
8,964✔
2002
    ) {
8,964✔
2003
        (self.op, self.lhs.clone(), self.rhs.clone())
8,964✔
2004
    }
8,964✔
2005
}
2006

2007
/// Internal helper. Extract one part of the expression tree, keeping the name map up to date.
2008
///
2009
/// Example: Given expr1 + expr2, each being [PyParameterExpression], we need the ability to
2010
/// extract one of the expressions with the proper name map.
2011
///
2012
/// Args:
2013
///     - joint_parameter_expr: The full expression, e.g. expr1 + expr2.
2014
///     - sub_expr: The sub expression, on whose symbols we restrict the name map.
2015
fn filter_name_map(
18,190✔
2016
    sub_expr: &SymbolExpr,
18,190✔
2017
    name_map: &HashMap<String, Symbol>,
18,190✔
2018
) -> ParameterExpression {
18,190✔
2019
    let sub_symbols: HashSet<&Symbol> = sub_expr.iter_symbols().collect();
18,190✔
2020
    let restricted_name_map: HashMap<String, Symbol> = name_map
18,190✔
2021
        .iter()
18,190✔
2022
        .filter(|(_, symbol)| sub_symbols.contains(*symbol))
23,318✔
2023
        .map(|(name, symbol)| (name.clone(), symbol.clone()))
18,190✔
2024
        .collect();
18,190✔
2025

2026
    ParameterExpression {
18,190✔
2027
        expr: sub_expr.clone(),
18,190✔
2028
        name_map: restricted_name_map,
18,190✔
2029
    }
18,190✔
2030
}
18,190✔
2031

2032
pub fn qpy_replay(
26,376✔
2033
    expr: &ParameterExpression,
26,376✔
2034
    name_map: &HashMap<String, Symbol>,
26,376✔
2035
    replay: &mut Vec<OPReplay>,
26,376✔
2036
) {
26,376✔
2037
    match &expr.expr {
26,376✔
2038
        SymbolExpr::Value(_) | SymbolExpr::Symbol(_) => {
16,782✔
2039
            // nothing to do here, we only need to traverse instructions
16,782✔
2040
        }
16,782✔
2041
        SymbolExpr::Unary { op, expr } => {
998✔
2042
            let op = match op {
998✔
2043
                symbol_expr::UnaryOp::Abs => OpCode::ABS,
6✔
2044
                symbol_expr::UnaryOp::Acos => OpCode::ACOS,
6✔
2045
                symbol_expr::UnaryOp::Asin => OpCode::ASIN,
6✔
2046
                symbol_expr::UnaryOp::Atan => OpCode::ATAN,
6✔
2047
                symbol_expr::UnaryOp::Conj => OpCode::CONJ,
6✔
2048
                symbol_expr::UnaryOp::Cos => OpCode::COS,
6✔
2049
                symbol_expr::UnaryOp::Exp => OpCode::EXP,
6✔
2050
                symbol_expr::UnaryOp::Log => OpCode::LOG,
6✔
2051
                symbol_expr::UnaryOp::Neg => OpCode::MUL,
922✔
2052
                symbol_expr::UnaryOp::Sign => OpCode::SIGN,
4✔
2053
                symbol_expr::UnaryOp::Sin => OpCode::SIN,
18✔
2054
                symbol_expr::UnaryOp::Tan => OpCode::TAN,
6✔
2055
            };
2056
            // TODO filter shouldn't be necessary for unary ops
2057
            let lhs = filter_name_map(expr, name_map);
998✔
2058

2059
            // recurse on the instruction
2060
            qpy_replay(&lhs, name_map, replay);
998✔
2061

2062
            let lhs_value = ParameterValueType::extract_from_expr(expr);
998✔
2063

2064
            // MUL is special: we implement ``neg`` as multiplication by -1
2065
            if let OpCode::MUL = &op {
998✔
2066
                replay.push(OPReplay {
922✔
2067
                    op,
922✔
2068
                    lhs: lhs_value,
922✔
2069
                    rhs: Some(ParameterValueType::Int(-1)),
922✔
2070
                });
922✔
2071
            } else {
922✔
2072
                replay.push(OPReplay {
76✔
2073
                    op,
76✔
2074
                    lhs: lhs_value,
76✔
2075
                    rhs: None,
76✔
2076
                });
76✔
2077
            }
76✔
2078
        }
2079
        SymbolExpr::Binary { op, lhs, rhs } => {
8,596✔
2080
            let lhs_value = ParameterValueType::extract_from_expr(lhs);
8,596✔
2081
            let rhs_value = ParameterValueType::extract_from_expr(rhs);
8,596✔
2082

2083
            // recurse on the parameter expressions
2084
            let lhs = filter_name_map(lhs, name_map);
8,596✔
2085
            let rhs = filter_name_map(rhs, name_map);
8,596✔
2086
            qpy_replay(&lhs, name_map, replay);
8,596✔
2087
            qpy_replay(&rhs, name_map, replay);
8,596✔
2088

2089
            // add the expression to the replay
2090
            match lhs_value {
8,342✔
2091
                None
2092
                | Some(ParameterValueType::Parameter(_))
2093
                | Some(ParameterValueType::VectorElement(_)) => {
2094
                    let op = match op {
1,278✔
2095
                        symbol_expr::BinaryOp::Add => OpCode::ADD,
744✔
2096
                        symbol_expr::BinaryOp::Sub => OpCode::SUB,
416✔
2097
                        symbol_expr::BinaryOp::Mul => OpCode::MUL,
86✔
2098
                        symbol_expr::BinaryOp::Div => OpCode::DIV,
12✔
2099
                        symbol_expr::BinaryOp::Pow => OpCode::POW,
20✔
2100
                    };
2101
                    replay.push(OPReplay {
1,278✔
2102
                        op,
1,278✔
2103
                        lhs: lhs_value,
1,278✔
2104
                        rhs: rhs_value,
1,278✔
2105
                    });
1,278✔
2106
                }
2107
                _ => {
2108
                    let op = match op {
7,318✔
2109
                        symbol_expr::BinaryOp::Add => OpCode::ADD,
578✔
2110
                        symbol_expr::BinaryOp::Sub => OpCode::RSUB,
120✔
2111
                        symbol_expr::BinaryOp::Mul => OpCode::MUL,
6,612✔
2112
                        symbol_expr::BinaryOp::Div => OpCode::RDIV,
4✔
2113
                        symbol_expr::BinaryOp::Pow => OpCode::RPOW,
4✔
2114
                    };
2115
                    if let OpCode::ADD | OpCode::MUL = op {
7,318✔
2116
                        replay.push(OPReplay {
7,190✔
2117
                            op,
7,190✔
2118
                            lhs: lhs_value,
7,190✔
2119
                            rhs: rhs_value,
7,190✔
2120
                        });
7,190✔
2121
                    } else {
7,190✔
2122
                        // this covers RSUB, RDIV, RPOW, hence we swap lhs and rhs
128✔
2123
                        replay.push(OPReplay {
128✔
2124
                            op,
128✔
2125
                            lhs: rhs_value,
128✔
2126
                            rhs: lhs_value,
128✔
2127
                        });
128✔
2128
                    }
128✔
2129
                }
2130
            }
2131
        }
2132
    }
2133
}
26,376✔
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