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

Qiskit / qiskit / 23304525254

19 Mar 2026 04:09PM UTC coverage: 87.346% (+0.04%) from 87.309%
23304525254

Pull #15361

github

web-flow
Merge 52d8c7053 into 13b23a355
Pull Request #15361: Add `qk_circuit_draw` function to the C API

15 of 17 new or added lines in 2 files covered. (88.24%)

102 existing lines in 7 files now uncovered.

103313 of 118280 relevant lines covered (87.35%)

1137052.73 hits per line

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

76.15
/crates/qpy/src/annotations.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
use crate::bytes::Bytes;
13
use crate::error::QpyError;
14
use hashbrown::HashMap;
15
use pyo3::prelude::*;
16
use pyo3::types::{PyDict, PyIterator, PyNotImplemented};
17
/// The purpose of AnnotationHandler is to keep track of all the annotation serializers/deserializers at once.
18
/// Each namespace has one potential serializer/deserializer corresponding to it, given in the factories list.
19
/// When serializing the first time a namespace is encountered, the serializer is generated from the factory,
20
/// but it remains inside `potential_serializers` as long as it did not manage to serialize any annotation.
21
/// The way to determine whether a serialization attempt succeeded is to check whether the result of `dump_annotation`
22
/// was `NotImplemented`.
23

24
#[derive(Debug)]
25
pub struct AnnotationHandler<'a> {
26
    pub annotation_factories: &'a Bound<'a, PyDict>,
27
    factories: HashMap<String, Py<PyAny>>,
28
    pub serializers: HashMap<String, (usize, Py<PyAny>)>,
29
    potential_serializers: HashMap<String, Py<PyAny>>,
30
    pub deserializers: Vec<Py<PyAny>>,
31
}
32

33
impl<'a> AnnotationHandler<'a> {
34
    pub fn new(annotation_factories: &'a Bound<'a, PyDict>) -> Self {
3,968✔
35
        let mut factories = HashMap::with_capacity(annotation_factories.len());
3,968✔
36
        for (key, value) in annotation_factories.iter() {
3,968✔
37
            if let Ok(key_string) = key.extract() {
96✔
38
                // we ignore non-string keys since they will not be invoked during serialization
96✔
39
                // where we choose serializer according to the namespace string
96✔
40
                factories.insert(key_string, value.clone().unbind());
96✔
41
            }
96✔
42
        }
43

44
        let serializers = HashMap::new();
3,968✔
45
        let potential_serializers = HashMap::new();
3,968✔
46
        // deserializers are created on demand based on the QPY annotation headers
47
        let deserializers = Vec::new();
3,968✔
48
        AnnotationHandler {
3,968✔
49
            annotation_factories,
3,968✔
50
            factories,
3,968✔
51
            serializers,
3,968✔
52
            potential_serializers,
3,968✔
53
            deserializers,
3,968✔
54
        }
3,968✔
55
    }
3,968✔
56
    pub fn serialize(&mut self, annotation: &Py<PyAny>) -> Result<(u32, Bytes), QpyError> {
38✔
57
        Python::attach(|py| {
38✔
58
            let annotation_namespace: String = annotation.getattr(py, "namespace")?.extract(py)?;
38✔
59
            let annotation_module = py.import("qiskit.circuit.annotation")?;
38✔
60
            let namespace_iter_func = annotation_module.getattr("iter_namespaces")?;
38✔
61
            let namespace_iter =
38✔
62
                PyIterator::from_object(&namespace_iter_func.call1((&annotation_namespace,))?)?;
38✔
63
            for namespace_res in namespace_iter {
52✔
64
                let namespace = namespace_res?;
52✔
65
                let namespace_string: String = namespace.clone().extract()?;
52✔
66
                // first check for an active serializer
67
                if let Some((index, serializer)) = self.serializers.get(&namespace_string) {
52✔
68
                    let result = serializer.call_method1(
12✔
69
                        py,
12✔
70
                        "dump_annotation",
71
                        (namespace.clone(), annotation),
12✔
72
                    )?;
×
73
                    if !result.is(PyNotImplemented::get(py)) {
12✔
74
                        let result_bytes: Bytes = result.extract(py)?;
10✔
75
                        return Ok((*index as u32, result_bytes));
10✔
76
                    }
2✔
77
                } else if let Some(serializer) = self.potential_serializers.get(&namespace_string) {
40✔
78
                    let result = serializer.call_method1(
×
79
                        py,
×
80
                        "dump_annotation",
81
                        (namespace.clone(), annotation),
×
82
                    )?;
×
83
                    if !result.is(PyNotImplemented::get(py)) {
×
84
                        let index = self.serializers.len();
×
85
                        let result_bytes: Bytes = result.extract(py)?;
×
86
                        self.serializers
×
87
                            .insert(namespace_string.clone(), (index, serializer.clone()));
×
88
                        return Ok((index as u32, result_bytes));
×
89
                    }
×
90
                }
91
                // no serializer, let's try to create one from the corresponding factory
92
                else if let Some(factory) = self.factories.get(&namespace_string) {
40✔
93
                    let serializer = factory.call0(py)?;
26✔
94
                    let result = serializer.call_method1(
26✔
95
                        py,
26✔
96
                        "dump_annotation",
97
                        (namespace.clone(), annotation),
26✔
98
                    )?;
×
99
                    if !result.is(PyNotImplemented::get(py)) {
26✔
100
                        let index = self.serializers.len();
26✔
101
                        let result_bytes: Bytes = result.extract(py)?;
26✔
102
                        self.serializers
26✔
103
                            .insert(namespace_string.clone(), (index, serializer));
26✔
104
                        return Ok((index as u32, result_bytes));
26✔
105
                    } else {
×
106
                        // This time the serializer failed, but we might want to try it again without
×
107
                        // having to reconstruct it from the factory
×
108
                        self.potential_serializers
×
109
                            .insert(namespace_string.clone(), serializer);
×
110
                    }
×
111
                }
14✔
112
            }
113
            Err(QpyError::AnnotationError(format!(
2✔
114
                "No configured annotation serializer could handle {}",
115
                annotation.bind(py).repr()?,
2✔
116
            )))
117
        })
38✔
118
    }
38✔
119

120
    pub fn load(&self, index: u32, payload: Bytes) -> Result<Py<PyAny>, QpyError> {
36✔
121
        if let Some(deserializer) = self.deserializers.get(index as usize) {
36✔
122
            Python::attach(|py| -> Result<Py<PyAny>, QpyError> {
36✔
123
                Ok(deserializer.call_method1(py, "load_annotation", (payload,))?)
36✔
124
            })
36✔
125
        } else {
126
            Err(QpyError::ConversionError(format!(
×
127
                "Annotation deserializer index {:?} out of range (0...{:?}), is too large and would overflow",
×
UNCOV
128
                index,
×
129
                self.deserializers.len()
×
UNCOV
130
            )))
×
131
        }
132
    }
36✔
133

134
    pub fn dump_serializers(&self) -> Result<Vec<(String, Bytes)>, QpyError> {
1,958✔
135
        // we need to be a little careful to keep the result sorted by index order
136
        let mut result: Vec<Option<(String, Bytes)>> = vec![None; self.serializers.len()];
1,958✔
137
        Python::attach(|py| -> Result<(), QpyError> {
1,958✔
138
            for (namespace, (index, serializer)) in &self.serializers {
1,958✔
139
                let state: Bytes = serializer.call_method0(py, "dump_state")?.extract(py)?;
26✔
140
                result[*index] = Some((namespace.clone(), state));
26✔
141
            }
142
            Ok(())
1,958✔
143
        })?;
1,958✔
144
        Ok(result.into_iter().flatten().collect())
1,958✔
145
    }
1,958✔
146

147
    pub fn load_deserializers(&mut self, data: Vec<(String, Bytes)>) -> Result<(), QpyError> {
1,532✔
148
        Python::attach(|py| -> Result<(), QpyError> {
1,532✔
149
            for (namespace, state) in data {
1,532✔
150
                if let Some(factory) = self.factories.get(&namespace) {
26✔
151
                    let serializer = factory.call0(py)?;
26✔
152
                    serializer.call_method1(
26✔
153
                        py,
26✔
154
                        "load_state",
155
                        (namespace.clone(), state.clone()),
26✔
UNCOV
156
                    )?;
×
157
                    self.deserializers.push(serializer);
26✔
UNCOV
158
                };
×
159
            }
160
            Ok(())
1,532✔
161
        })?;
1,532✔
162
        Ok(())
1,532✔
163
    }
1,532✔
164
}
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