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

Qiskit / qiskit / 25723423515

12 May 2026 08:40AM UTC coverage: 87.644% (+0.03%) from 87.619%
25723423515

push

github

web-flow
Fix: Use generic errors in the variable mapper and expr. (#16132)

While in the past we relied on `PyResult` to handle all of the errors as python exceptions, we can now remove some of the dependencies in parts of our system.
After some evaluation, it turns out we can use generic errors in the `VariableMapper` and `Expr` which frees us from needing python in order to use these components.

This was discovered during efforts to separate `DAGCircuit`'s error messages from always using `PyErr`.

24 of 25 new or added lines in 2 files covered. (96.0%)

4 existing lines in 3 files now uncovered.

107054 of 122147 relevant lines covered (87.64%)

960869.21 hits per line

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

63.51
/crates/circuit/src/variable_mapper.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2025
4
//
5
// This code is licensed under the Apache License, Version 2.0. You may
6
// obtain a copy of this license in the LICENSE.txt file in the root directory
7
// of this source tree or at https://www.apache.org/licenses/LICENSE-2.0.
8
//
9
// Any modifications or derivative works of this code must retain this
10
// copyright notice, and modified files need to carry a notice indicating
11
// that they have been altered from the originals.
12

13
use crate::bit::{ClassicalRegister, Register, ShareableClbit};
14
use crate::classical::expr;
15
use crate::operations::{Condition, SwitchTarget};
16
use hashbrown::{HashMap, HashSet};
17
use num_bigint::BigUint;
18
use num_traits::Num;
19
use std::cell::RefCell;
20
use std::error::Error;
21

22
pub(crate) struct VariableMapper {
23
    target_cregs: Vec<ClassicalRegister>,
24
    register_map: RefCell<HashMap<String, ClassicalRegister>>,
25
    bit_map: HashMap<ShareableClbit, ShareableClbit>,
26
    var_map: HashMap<expr::Var, expr::Var>,
27
    stretch_map: HashMap<expr::Stretch, expr::Stretch>,
28
}
29

30
impl VariableMapper {
31
    /// Constructs a new mapper.
32
    ///
33
    /// The `stretch_map` is only used for direct calls to [VariableMapper::map_expr]
34
    /// since `condition`s and `target`s expressions are never durations. Provide
35
    /// [None] if you don't need this.
36
    pub fn new(
116,136✔
37
        target_cregs: Vec<ClassicalRegister>,
116,136✔
38
        bit_map: HashMap<ShareableClbit, ShareableClbit>,
116,136✔
39
        var_map: HashMap<expr::Var, expr::Var>,
116,136✔
40
        stretch_map: Option<HashMap<expr::Stretch, expr::Stretch>>,
116,136✔
41
    ) -> Self {
116,136✔
42
        Self {
116,136✔
43
            target_cregs,
116,136✔
44
            register_map: RefCell::default(),
116,136✔
45
            bit_map,
116,136✔
46
            var_map,
116,136✔
47
            stretch_map: stretch_map.unwrap_or_default(),
116,136✔
48
        }
116,136✔
49
    }
116,136✔
50

51
    /// Map the given `condition` so that it only references variables in the destination
52
    /// circuit (as given to this class on initialization).
53
    ///
54
    /// If `allow_reorder` is `true`, then when a legacy condition (the two-tuple form) is made
55
    /// on a register that has a counterpart in the destination with all the same (mapped) bits but
56
    /// in a different order, then that register will be used and the value suitably modified to
57
    /// make the equality condition work.  This is maintaining legacy (tested) behavior of
58
    /// [DAGCircuit::compose]; nowhere else does this, and in general this would require *far*
59
    /// more complex classical rewriting than Qiskit needs to worry about in the full expression
60
    /// era.
61
    pub fn map_condition<F, E>(
52✔
62
        &self,
52✔
63
        condition: &Condition,
52✔
64
        allow_reorder: bool,
52✔
65
        mut add_register: F,
52✔
66
    ) -> Result<Condition, E>
52✔
67
    where
52✔
68
        F: FnMut(&ClassicalRegister) -> Result<(), E>,
52✔
69
        E: Error,
52✔
70
    {
71
        Ok(match condition {
52✔
72
            Condition::Bit(target, value) => {
38✔
73
                Condition::Bit(self.bit_map.get(target).unwrap().clone(), *value)
38✔
74
            }
75
            Condition::Expr(e) => Condition::Expr(self.map_expr(e, &mut add_register)?),
12✔
76
            Condition::Register(target, value) => {
2✔
77
                if !allow_reorder {
2✔
78
                    return Ok(Condition::Register(
79
                        self.map_register(target, &mut add_register)?,
2✔
80
                        value.clone(),
2✔
81
                    ));
82
                }
×
83
                // This is maintaining the legacy behavior of `DAGCircuit.compose`.  We don't
84
                // attempt to speed-up this lookup with a cache, since that would just make the more
85
                // standard cases more annoying to deal with.
86

87
                let mapped_bits_order = target
×
88
                    .bits()
×
89
                    .map(|b| self.bit_map.get(&b).unwrap().clone())
×
90
                    .collect::<Vec<_>>();
×
91
                let mapped_bits_set: HashSet<ShareableClbit> =
×
92
                    mapped_bits_order.iter().cloned().collect();
×
93

94
                let mapped_theirs = self
×
95
                    .target_cregs
×
96
                    .iter()
×
97
                    .find(|register| {
×
98
                        let register_set: HashSet<ShareableClbit> = register.iter().collect();
×
99
                        mapped_bits_set == register_set
×
100
                    })
×
101
                    .cloned()
×
NEW
102
                    .map(Ok::<_, E>)
×
103
                    .unwrap_or_else(|| {
×
104
                        let mapped_theirs =
×
105
                            ClassicalRegister::new_alias(None, mapped_bits_order.clone());
×
106
                        add_register(&mapped_theirs)?;
×
107
                        Ok(mapped_theirs)
×
108
                    })?;
×
109

110
                let new_order: HashMap<ShareableClbit, usize> = mapped_bits_order
×
111
                    .into_iter()
×
112
                    .enumerate()
×
113
                    .map(|(i, bit)| (bit, i))
×
114
                    .collect();
×
115

116
                // Build the little-endian bit string
117
                let value_bits: Vec<char> = format!("{:0width$b}", value, width = target.len())
×
118
                    .chars()
×
119
                    .rev()
×
120
                    .collect();
×
121

122
                // Reorder bits and reverse again to go back to big-endian for final conversion
123
                let mapped_str: String = mapped_theirs
×
124
                    .iter() // TODO: we should probably not need to collect to Vec here. Why do we?
×
125
                    .collect::<Vec<_>>()
×
126
                    .into_iter()
×
127
                    .map(|bit| value_bits[*new_order.get(&bit).unwrap()])
×
128
                    .rev()
×
129
                    .collect();
×
130

131
                Condition::Register(
×
132
                    mapped_theirs,
×
133
                    BigUint::from_str_radix(&mapped_str, 2).unwrap(),
×
134
                )
×
135
            }
136
        })
137
    }
52✔
138

139
    /// Map the real-time variables in a `target` of a `SwitchCaseOp` to the new
140
    /// circuit.
141
    pub fn map_target<F, E>(
12✔
142
        &self,
12✔
143
        target: &SwitchTarget,
12✔
144
        mut add_register: F,
12✔
145
    ) -> Result<SwitchTarget, E>
12✔
146
    where
12✔
147
        F: FnMut(&ClassicalRegister) -> Result<(), E>,
12✔
148
        E: Error,
12✔
149
    {
150
        Ok(match target {
12✔
151
            SwitchTarget::Bit(bit) => SwitchTarget::Bit(self.bit_map.get(bit).cloned().unwrap()),
×
152
            SwitchTarget::Register(register) => {
×
153
                SwitchTarget::Register(self.map_register(register, &mut add_register)?)
×
154
            }
155
            SwitchTarget::Expr(expr) => SwitchTarget::Expr(self.map_expr(expr, &mut add_register)?),
12✔
156
        })
157
    }
12✔
158

159
    /// Map the variables in an [expr::Expr] node to the new circuit.
160
    pub fn map_expr<F, E>(&self, expr: &expr::Expr, mut add_register: F) -> Result<expr::Expr, E>
24✔
161
    where
24✔
162
        F: FnMut(&ClassicalRegister) -> Result<(), E>,
24✔
163
        E: Error,
24✔
164
    {
165
        let mut mapped = expr.clone();
24✔
166
        mapped.visit_mut(|e| match e {
58✔
167
            expr::ExprRefMut::Var(var) => match var {
28✔
168
                expr::Var::Standalone { .. } => {
169
                    if let Some(mapping) = self.var_map.get(var).cloned() {
6✔
170
                        *var = mapping;
6✔
171
                    }
6✔
172
                    Ok(())
6✔
173
                }
174
                expr::Var::Bit { bit } => {
12✔
175
                    let bit = self.bit_map.get(bit).cloned().unwrap();
12✔
176
                    *var = expr::Var::Bit { bit };
12✔
177
                    Ok(())
12✔
178
                }
179
                expr::Var::Register { register, ty } => {
10✔
180
                    let ty = *ty;
10✔
181
                    let register = self.map_register(register, &mut add_register)?;
10✔
182
                    *var = expr::Var::Register { register, ty };
10✔
183
                    Ok(())
10✔
184
                }
185
            },
186
            expr::ExprRefMut::Stretch(stretch) => {
×
187
                if let Some(mapping) = self.stretch_map.get(stretch).cloned() {
×
188
                    *stretch = mapping;
×
189
                }
×
190
                Ok(())
×
191
            }
192
            _ => Ok(()),
30✔
193
        })?;
58✔
194
        Ok(mapped)
24✔
195
    }
24✔
196

197
    /// Map the target's registers to suitable equivalents in the destination, adding an
198
    /// extra one if there's no exact match."""
199
    fn map_register<F, E>(
12✔
200
        &self,
12✔
201
        theirs: &ClassicalRegister,
12✔
202
        mut add_register: F,
12✔
203
    ) -> Result<ClassicalRegister, E>
12✔
204
    where
12✔
205
        F: FnMut(&ClassicalRegister) -> Result<(), E>,
12✔
206
        E: Error,
12✔
207
    {
208
        if let Some(mapped_theirs) = self.register_map.borrow().get(theirs.name()) {
12✔
209
            return Ok(mapped_theirs.clone());
2✔
210
        }
10✔
211

212
        let mapped_bits: Vec<_> = theirs.iter().map(|b| self.bit_map[&b].clone()).collect();
22✔
213
        let mapped_theirs = self
10✔
214
            .target_cregs
10✔
215
            .iter()
10✔
216
            .find(|register| {
14✔
217
                let register: Vec<_> = register.bits().collect();
14✔
218
                mapped_bits == register
14✔
219
            })
14✔
220
            .cloned()
10✔
221
            .map(Ok::<_, E>)
10✔
222
            .unwrap_or_else(|| {
10✔
223
                let mapped_theirs = ClassicalRegister::new_alias(None, mapped_bits.clone());
×
224
                add_register(&mapped_theirs)?;
×
225
                Ok(mapped_theirs)
×
226
            })?;
×
227

228
        self.register_map
10✔
229
            .borrow_mut()
10✔
230
            .insert(theirs.name().to_string(), mapped_theirs.clone());
10✔
231
        Ok(mapped_theirs)
10✔
232
    }
12✔
233
}
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