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

Qiskit / qiskit / 23183378289

17 Mar 2026 07:33AM UTC coverage: 87.519% (-0.02%) from 87.543%
23183378289

Pull #15807

github

web-flow
Merge e052a507c into a264eb7fa
Pull Request #15807: Fix approximate-by-default behaviour of `UnitarySynthesis`

59 of 67 new or added lines in 5 files covered. (88.06%)

1195 existing lines in 9 files now uncovered.

101149 of 115574 relevant lines covered (87.52%)

1166293.08 hits per line

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

96.85
/crates/qpy/src/expr.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2025
4
//
5
// This code is licensed under the Apache License, Version 2.0. You may
6
// obtain a copy of this license in the LICENSE.txt file in the root directory
7
// of this source tree or at 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
// methods for serialization/deserialization of Expression
14
use crate::UnsupportedFeatureForVersion;
15
use crate::formats::{
16
    ExpressionElementPack, ExpressionTypePack, ExpressionValueElementPack,
17
    ExpressionVarElementPack, ExpressionVarRegisterPack, to_binrw_error,
18
};
19
use crate::value::{
20
    QPYReadData, QPYWriteData, pack_biguint, pack_duration, unpack_biguint, unpack_duration,
21
};
22
use binrw::{BinRead, BinResult, BinWrite, Endian, Error};
23
use num_bigint::BigUint;
24
use pyo3::prelude::*;
25
use qiskit_circuit::Clbit;
26
use qiskit_circuit::classical::expr::{
27
    Binary, BinaryOp, Cast, Expr, Index, Unary, UnaryOp, Value, Var,
28
};
29
use qiskit_circuit::classical::types::Type;
30
use qiskit_circuit::duration::Duration;
31
use std::io::{Read, Seek, Write};
32

33
// packed expression types implicitly contain the magic number identifying them in the qpy file
34
pub(crate) fn pack_expression_type(ty: &Type) -> ExpressionTypePack {
920✔
35
    match ty {
920✔
36
        Type::Bool => ExpressionTypePack::Bool,
346✔
37
        Type::Uint(width) => ExpressionTypePack::Int(*width as u32),
300✔
38
        Type::Duration => ExpressionTypePack::Duration,
146✔
39
        Type::Float => ExpressionTypePack::Float,
128✔
40
    }
41
}
920✔
42

43
pub(crate) fn unpack_expression_type(type_pack: ExpressionTypePack) -> Type {
1,520✔
44
    match type_pack {
1,520✔
45
        ExpressionTypePack::Bool => Type::Bool,
578✔
46
        ExpressionTypePack::Duration => Type::Duration,
274✔
47
        ExpressionTypePack::Float => Type::Float,
256✔
48
        ExpressionTypePack::Int(width) => Type::Uint(width as u16),
412✔
49
    }
50
}
1,520✔
51

52
pub(crate) fn pack_expression_value(
332✔
53
    value: &Value,
332✔
54
    qpy_data: &QPYWriteData,
332✔
55
) -> PyResult<ExpressionElementPack> {
332✔
56
    let (ty, value_pack) = match value {
332✔
57
        Value::Uint { raw, ty } => {
132✔
58
            match ty {
132✔
59
                Type::Bool => (ty, ExpressionValueElementPack::Bool(raw.to_bytes_le()[0])), // effectively truncating modulo 256
18✔
60
                Type::Uint(_) => (ty, ExpressionValueElementPack::Int(pack_biguint(raw))),
114✔
61
                _ => (ty, ExpressionValueElementPack::Bool(raw.to_bytes_le()[0])), // TODO: should this be different?
×
62
            }
63
        }
64
        Value::Float { raw, ty } => (ty, ExpressionValueElementPack::Float(*raw)),
96✔
65
        Value::Duration(duration) => {
104✔
66
            if qpy_data.version < 16 && matches!(duration, Duration::ps(_)) {
104✔
67
                return Err(UnsupportedFeatureForVersion::new_err((
×
68
                    "Duration variant 'Duration.ps'",
×
69
                    16,
×
70
                    qpy_data.version,
×
71
                )));
×
72
            }
104✔
73
            (
104✔
74
                &Type::Duration,
104✔
75
                ExpressionValueElementPack::Duration(pack_duration(duration)),
104✔
76
            )
104✔
77
        }
78
    };
79
    Ok(ExpressionElementPack::Value(
332✔
80
        pack_expression_type(ty),
332✔
81
        value_pack,
332✔
82
    ))
332✔
83
}
332✔
84

85
pub(crate) fn unpack_expression_value(
568✔
86
    value_type_pack: ExpressionTypePack,
568✔
87
    value_element_pack: ExpressionValueElementPack,
568✔
88
) -> Value {
568✔
89
    let ty = unpack_expression_type(value_type_pack);
568✔
90
    match value_element_pack {
568✔
91
        ExpressionValueElementPack::Bool(val) => Value::Uint {
18✔
92
            raw: BigUint::from_bytes_le(&[val]),
18✔
93
            ty,
18✔
94
        },
18✔
95
        ExpressionValueElementPack::Int(val) => Value::Uint {
158✔
96
            raw: unpack_biguint(val),
158✔
97
            ty,
158✔
98
        },
158✔
99
        ExpressionValueElementPack::Duration(duration) => {
200✔
100
            Value::Duration(unpack_duration(duration))
200✔
101
        }
102
        ExpressionValueElementPack::Float(val) => Value::Float { raw: val, ty },
192✔
103
    }
104
}
568✔
105

106
pub(crate) fn pack_expression_var(var: &Var, qpy_data: &QPYWriteData) -> ExpressionElementPack {
248✔
107
    let (ty, value_pack) = match var {
248✔
108
        Var::Bit { bit } => (
46✔
109
            &Type::Bool,
46✔
110
            ExpressionVarElementPack::Clbit(qpy_data.circuit_data.clbits().find(bit).unwrap().0),
46✔
111
        ),
46✔
112
        Var::Register { register, ty } => (
62✔
113
            ty,
62✔
114
            ExpressionVarElementPack::Register(ExpressionVarRegisterPack {
62✔
115
                name: register.name().to_string(),
62✔
116
            }),
62✔
117
        ),
62✔
118
        Var::Standalone { uuid, name: _, ty } => (
140✔
119
            ty,
140✔
120
            ExpressionVarElementPack::Uuid(*qpy_data.standalone_var_indices.get(uuid).unwrap()),
140✔
121
        ),
140✔
122
    };
123
    ExpressionElementPack::Var(pack_expression_type(ty), value_pack)
248✔
124
}
248✔
125

126
pub(crate) fn unpack_expression_var(
352✔
127
    var_type_pack: ExpressionTypePack,
352✔
128
    var_element_pack: ExpressionVarElementPack,
352✔
129
    qpy_data: &QPYReadData,
352✔
130
) -> Var {
352✔
131
    let ty = unpack_expression_type(var_type_pack);
352✔
132
    match var_element_pack {
352✔
133
        ExpressionVarElementPack::Clbit(index) => Var::Bit {
76✔
134
            bit: qpy_data
76✔
135
                .circuit_data
76✔
136
                .clbits()
76✔
137
                .get(Clbit(index))
76✔
138
                .unwrap()
76✔
139
                .clone(),
76✔
140
        }, // TODO: error handling?
76✔
141
        ExpressionVarElementPack::Register(packed_register) => Var::Register {
104✔
142
            register: qpy_data
104✔
143
                .circuit_data
104✔
144
                .cregs_data()
104✔
145
                .get(packed_register.name.as_str())
104✔
146
                .unwrap()
104✔
147
                .clone(),
104✔
148
            ty,
104✔
149
        }, // TODO: can we avoid cloning?
104✔
150
        ExpressionVarElementPack::Uuid(key) => {
172✔
151
            let var = qpy_data.standalone_vars.get(&key).unwrap(); // note: this is not an actual expr::Var; merely a key for this var inside the circuit data
172✔
152
            qpy_data
172✔
153
                .circuit_data
172✔
154
                .vars_stretches_view()
172✔
155
                .vars()
172✔
156
                .get(*var)
172✔
157
                .unwrap()
172✔
158
                .clone() // TODO: can we avoid cloning?
172✔
159
        }
160
    }
161
}
352✔
162

163
pub(crate) fn write_expression<W: Write + Seek>(
932✔
164
    exp: &Expr,
932✔
165
    writer: &mut W,
932✔
166
    endian: Endian,
932✔
167
    (qpy_data,): (&QPYWriteData,),
932✔
168
) -> binrw::BinResult<()> {
932✔
169
    match exp {
932✔
170
        Expr::Value(val) => {
332✔
171
            pack_expression_value(val, qpy_data)
332✔
172
                .map_err(|e| to_binrw_error(writer, e))?
332✔
173
                .write_options(writer, endian, ())?;
332✔
174
        }
175
        Expr::Var(var) => {
248✔
176
            pack_expression_var(var, qpy_data).write_options(writer, endian, ())?;
248✔
177
        }
178
        Expr::Stretch(stretch) => {
12✔
179
            ExpressionElementPack::Stretch(
12✔
180
                ExpressionTypePack::Duration,
12✔
181
                qpy_data.standalone_var_indices[&stretch.uuid],
12✔
182
            )
12✔
183
            .write_options(writer, endian, ())?;
12✔
184
        }
185
        Expr::Index(index_node) => {
4✔
186
            ExpressionElementPack::Index(pack_expression_type(&index_node.ty)).write_options(
4✔
187
                writer,
4✔
188
                endian,
4✔
189
                (),
4✔
UNCOV
190
            )?;
×
191
            write_expression(&index_node.target, writer, endian, (qpy_data,))?;
4✔
192
            write_expression(&index_node.index, writer, endian, (qpy_data,))?;
4✔
193
        }
194
        Expr::Cast(cast_node) => {
12✔
195
            ExpressionElementPack::Cast(
12✔
196
                pack_expression_type(&cast_node.ty),
12✔
197
                cast_node.implicit as u8,
12✔
198
            )
12✔
199
            .write_options(writer, endian, ())?;
12✔
200
            write_expression(&cast_node.operand, writer, endian, (qpy_data,))?;
12✔
201
        }
202
        Expr::Unary(unary_node) => {
28✔
203
            ExpressionElementPack::Unary(pack_expression_type(&unary_node.ty), unary_node.op as u8)
28✔
204
                .write_options(writer, endian, ())?;
28✔
205
            write_expression(&unary_node.operand, writer, endian, (qpy_data,))?;
28✔
206
        }
207
        Expr::Binary(binary_node) => {
296✔
208
            ExpressionElementPack::Binary(
296✔
209
                pack_expression_type(&binary_node.ty),
296✔
210
                binary_node.op as u8,
296✔
211
            )
296✔
212
            .write_options(writer, endian, ())?;
296✔
213
            write_expression(&binary_node.left, writer, endian, (qpy_data,))?;
296✔
214
            write_expression(&binary_node.right, writer, endian, (qpy_data,))?;
296✔
215
        }
216
    };
217
    Ok(())
932✔
218
}
932✔
219

220
pub(crate) fn read_expression<R: Read + Seek>(
1,532✔
221
    reader: &mut R,
1,532✔
222
    endian: Endian,
1,532✔
223
    (qpy_data,): (&QPYReadData,),
1,532✔
224
) -> BinResult<Expr> {
1,532✔
225
    let exp_element = ExpressionElementPack::read_options(reader, endian, ())?;
1,532✔
226
    match exp_element {
1,532✔
227
        ExpressionElementPack::Value(value_type_pack, value_element_pack) => Ok(Expr::Value(
568✔
228
            unpack_expression_value(value_type_pack, value_element_pack),
568✔
229
        )),
568✔
230
        ExpressionElementPack::Var(var_type_pack, var_element_pack) => Ok(Expr::Var(
352✔
231
            unpack_expression_var(var_type_pack, var_element_pack, qpy_data),
352✔
232
        )),
352✔
233
        ExpressionElementPack::Stretch(_stretch_type_pack, key) => {
12✔
234
            let stretch = qpy_data.standalone_stretches.get(&key).unwrap();
12✔
235
            Ok(Expr::Stretch(
12✔
236
                qpy_data
12✔
237
                    .circuit_data
12✔
238
                    .vars_stretches_view()
12✔
239
                    .stretches()
12✔
240
                    .get(*stretch)
12✔
241
                    .unwrap()
12✔
242
                    .clone(),
12✔
243
            )) // TODO: can we avoid cloning?
12✔
244
        }
245
        ExpressionElementPack::Index(index_type_pack) => {
4✔
246
            let target = read_expression(reader, endian, (qpy_data,))?;
4✔
247
            let index = read_expression(reader, endian, (qpy_data,))?;
4✔
248
            let constant = target.is_const() && index.is_const();
4✔
249
            Ok(Expr::Index(Box::new(Index {
4✔
250
                target,
4✔
251
                index,
4✔
252
                ty: unpack_expression_type(index_type_pack),
4✔
253
                constant,
4✔
254
            })))
4✔
255
        }
256
        ExpressionElementPack::Cast(cast_type_pack, implicit) => {
18✔
257
            let operand = read_expression(reader, endian, (qpy_data,))?;
18✔
258
            let constant = operand.is_const();
18✔
259
            Ok(Expr::Cast(Box::new(Cast {
18✔
260
                operand,
18✔
261
                ty: unpack_expression_type(cast_type_pack),
18✔
262
                constant,
18✔
263
                implicit: implicit != 0,
18✔
264
            })))
18✔
265
        }
266
        ExpressionElementPack::Unary(unary_type_pack, op) => {
52✔
267
            let operand = read_expression(reader, endian, (qpy_data,))?;
52✔
268
            let constant = operand.is_const();
52✔
269
            Ok(Expr::Unary(Box::new(Unary {
52✔
270
                op: UnaryOp::from_u8(op).map_err(|_| Error::NoVariantMatch { pos: (0) })?,
52✔
271
                operand,
52✔
272
                ty: unpack_expression_type(unary_type_pack),
52✔
273
                constant,
52✔
274
            })))
275
        }
276
        ExpressionElementPack::Binary(binary_type_pack, op) => {
526✔
277
            let left = read_expression(reader, endian, (qpy_data,))?;
526✔
278
            let right = read_expression(reader, endian, (qpy_data,))?;
526✔
279
            let constant = left.is_const() && right.is_const();
526✔
280
            Ok(Expr::Binary(Box::new(Binary {
526✔
281
                op: BinaryOp::from_u8(op).map_err(|_| Error::NoVariantMatch { pos: (0) })?,
526✔
282
                left,
526✔
283
                right,
526✔
284
                ty: unpack_expression_type(binary_type_pack),
526✔
285
                constant,
526✔
286
            })))
287
        }
288
    }
289
}
1,532✔
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