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

Qiskit / qiskit / 26472749467

26 May 2026 08:17PM UTC coverage: 87.498% (-0.1%) from 87.605%
26472749467

Pull #16224

github

web-flow
Merge 303f48ca9 into 55a4f6d0e
Pull Request #16224: Add `QuantumProgram` impl of `ProgramNode`

1220 of 1534 new or added lines in 8 files covered. (79.53%)

27 existing lines in 5 files now uncovered.

109775 of 125460 relevant lines covered (87.5%)

950016.56 hits per line

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

90.91
/crates/providers/src/program_node.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2026
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::data_tree::DataTree;
14
use crate::tensor::{DType, Tensor, TensorType};
15
use thiserror::Error;
16

17
/// Errors returned by [`ProgramNode`] implementations when the input tree does
18
/// not match the contract declared by [`ProgramNode::input_types`].
19
#[derive(Debug, Clone, PartialEq, Eq, Error)]
20
pub enum ProgramNodeError {
21
    /// A required named input was not present in the input tree.
22
    #[error("missing required input {key:?}")]
23
    MissingInput { key: String },
24
    /// An entry expected to be a leaf was a branch. An empty `key` indicates the
25
    /// top-level `args` itself was expected to be a leaf.
26
    #[error("expected a leaf at {key:?}, found a branch")]
27
    ExpectedLeaf { key: String },
28
    /// A tensor input had an unexpected dtype. An empty `key` refers to the
29
    /// top-level `args` leaf.
30
    #[error("unexpected dtype at {key:?}: expected {expected}, found {actual}")]
31
    UnexpectedDType {
32
        key: String,
33
        expected: String,
34
        actual: DType,
35
    },
36
}
37

38
/// Extract a leaf [`Tensor`] from a [`DataTree`] of inputs.
39
///
40
/// If `key` is non-empty, looks up the entry by string key (see
41
/// [`DataTree::get_by_str_key`]) and returns the leaf there. If `key` is empty,
42
/// returns the leaf at `args` itself — useful for nodes that take a single
43
/// unnamed tensor as input.
44
pub fn require_leaf_arg<'a>(
60✔
45
    args: &'a DataTree<Tensor>,
60✔
46
    key: &str,
60✔
47
) -> Result<&'a Tensor, ProgramNodeError> {
60✔
48
    let entry = if key.is_empty() {
60✔
49
        args
21✔
50
    } else {
51
        args.get_by_str_key(key)
39✔
52
            .ok_or_else(|| ProgramNodeError::MissingInput { key: key.into() })?
39✔
53
    };
54
    match entry {
59✔
55
        DataTree::Leaf(t) => Ok(t),
56✔
56
        DataTree::Branch(_) => Err(ProgramNodeError::ExpectedLeaf { key: key.into() }),
3✔
57
    }
58
}
60✔
59

60
/// Extract a leaf [`Tensor`] from a [`DataTree`] of inputs, and expect a type.
61
///
62
/// This function is [`require_leaf_arg`] with an additional type check.
63
pub fn require_typed_leaf_arg<'a>(
14✔
64
    args: &'a DataTree<Tensor>,
14✔
65
    key: &str,
14✔
66
    expected_dtype: DType,
14✔
67
) -> Result<&'a Tensor, ProgramNodeError> {
14✔
68
    let arg = require_leaf_arg(args, key)?;
14✔
69
    if arg.dtype() != expected_dtype {
13✔
70
        return Err(ProgramNodeError::UnexpectedDType {
1✔
71
            key: key.into(),
1✔
72
            expected: expected_dtype.to_string(),
1✔
73
            actual: arg.dtype(),
1✔
74
        });
1✔
75
    }
12✔
76
    Ok(arg)
12✔
77
}
14✔
78

79
/// Returned by [`ProgramNode::call`] implementations on nodes whose
80
/// [`implements_call`](ProgramNode::implements_call) is `false`.
81
#[derive(Debug, Clone, Error)]
82
#[error("node {0:?} does not implement call()")]
83
pub struct MissingCallError(pub String);
84

85
impl MissingCallError {
86
    /// Construct a new [`MissingCallError`] tagged with the offending node's full name.
NEW
87
    pub fn new(name: impl Into<String>) -> Self {
×
NEW
88
        Self(name.into())
×
NEW
89
    }
×
90
}
91

92
/// A node in a quantum program graph that transforms tensors.
93
pub trait ProgramNode {
94
    type CallError;
95

96
    /// The name of this program node.
97
    fn name(&self) -> &str;
98

99
    /// The namespace this program node belongs to.
100
    fn namespace(&self) -> &str;
101

102
    /// The namespace and name as one string.
103
    fn full_name(&self) -> String {
2✔
104
        format!("{}.{}", self.namespace(), self.name())
2✔
105
    }
2✔
106

107
    /// The inputs expected at call time.
108
    fn input_types(&self) -> &DataTree<TensorType>;
109

110
    /// The outputs promised on call return.
111
    fn output_types(&self) -> &DataTree<TensorType>;
112

113
    /// Whether this program node implements the call method.
114
    fn implements_call(&self) -> bool;
115

116
    /// The action of this program node.
117
    fn call(&self, args: &DataTree<Tensor>) -> Result<DataTree<Tensor>, Self::CallError>;
118
}
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