microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.22.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/pip/src/generic_estimator/counts.rs

186lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use pyo3::{
5 Bound, PyAny, PyResult,
6 types::{PyAnyMethods, PyDict},
7};
8use resource_estimator::estimates::{ErrorBudget, ErrorBudgetStrategy, Overhead};
9
10use crate::generic_estimator::utils::maybe_extract_and_check_method;
11
12use super::utils::extract_and_check_method;
13
14/// A wrapper around a Python instance to compute post-layout logical overhead
15/// for generic resource estimation.
16///
17/// The Python instance must implement three methods to compute the number of
18/// logical qubits, the logical depth, and the number of magic states. For the
19/// last two methods, the method receives a parameter `budget`, which is a
20/// dictionary with access to the chosen error budget for logical errors
21/// (`"logical"`), rotation synthesis budget (`"rotations"`), and magic state
22/// budget (`"magic_states"`):
23///
24/// ```python
25/// def logical_qubits(self):
26/// # must return an int
27/// ...
28///
29/// def logical_depth(self, budget):
30/// # must return an int
31/// #
32/// # budget is a dictionary of the form
33/// # {"logical": ..., "rotations": ..., "magic_states": ...}
34/// ...
35///
36/// def num_magic_states(self, budget, index):
37/// # must return an int
38/// #
39/// # here index is the type of magic state; normally this number is 0,
40/// # as there is only one magic state, but if there are multiple, this
41/// # is some number starting from 0.
42/// ...
43/// ```
44///
45/// It's important to note that the number of factory builders provided in the
46/// `estimate_generic` function determine how many magic states are being
47/// requested from the logical counts model. If only one factory is provided,
48/// then the `num_magic_states` method is only requested for the index `0` and
49/// not for any other indices.
50///
51/// Optionally, the instance may implement a method called `algorithm_overhead`,
52/// which also takes the error budget parameter `parameter` and returns a Python
53/// dictionary that is serialized into the estimation result, accessible via the
54/// key `"algorithmOverhead"`. This can contain layout-specific variables that
55/// are interesting statistics to expose.
56///
57/// ```python
58/// def algorithm_overhead(self, budget):
59/// # returns a serializable Python dictionary
60/// ...
61/// ```
62pub struct PythonCounts<'py> {
63 counts: Bound<'py, PyAny>,
64 logical_qubits_method: Bound<'py, PyAny>,
65 logical_depth_method: Bound<'py, PyAny>,
66 num_magic_states_method: Bound<'py, PyAny>,
67 prune_error_budget: Option<Bound<'py, PyAny>>,
68}
69
70impl<'py> PythonCounts<'py> {
71 pub fn from_bound(counts: Bound<'py, PyAny>) -> PyResult<Self> {
72 let logical_qubits_method = extract_and_check_method(&counts, "logical_qubits")?;
73 let logical_depth_method = extract_and_check_method(&counts, "logical_depth")?;
74 let num_magic_states_method = extract_and_check_method(&counts, "num_magic_states")?;
75 let prune_error_budget = maybe_extract_and_check_method(&counts, "prune_error_budget")?;
76
77 Ok(Self {
78 counts,
79 logical_qubits_method,
80 logical_depth_method,
81 num_magic_states_method,
82 prune_error_budget,
83 })
84 }
85
86 fn convert_budget(&self, budget: &ErrorBudget) -> PyResult<Bound<'py, PyDict>> {
87 let dict = PyDict::new(self.counts.py());
88
89 dict.set_item("logical", budget.logical())?;
90 dict.set_item("rotations", budget.rotations())?;
91 dict.set_item("magic_states", budget.magic_states())?;
92
93 Ok(dict)
94 }
95
96 fn convert_error_budget_strategy(strategy: ErrorBudgetStrategy) -> u32 {
97 match strategy {
98 ErrorBudgetStrategy::Static => 0,
99 ErrorBudgetStrategy::PruneLogicalAndRotations => 1,
100 }
101 }
102
103 pub fn algorithm_overhead(
104 &self,
105 error_budget: &ErrorBudget,
106 ) -> PyResult<Option<Bound<'py, PyAny>>> {
107 if let Ok(result) = self
108 .counts
109 .getattr("algorithm_overhead")
110 .and_then(|f| f.call1((self.convert_budget(error_budget)?,)))
111 {
112 Ok(Some(result))
113 } else {
114 Ok(None)
115 }
116 }
117}
118
119impl Overhead for PythonCounts<'_> {
120 fn logical_qubits(&self) -> Result<u64, String> {
121 let result = self
122 .logical_qubits_method
123 .call0()
124 .map_err(|e| e.to_string())?;
125 result.extract().map_err(|e| e.to_string())
126 }
127
128 fn logical_depth(&self, budget: &ErrorBudget) -> Result<u64, String> {
129 let budget = self.convert_budget(budget).map_err(|e| e.to_string())?;
130
131 let result = self
132 .logical_depth_method
133 .call1((budget,))
134 .map_err(|e| e.to_string())?;
135
136 result.extract().map_err(|e| e.to_string())
137 }
138
139 fn num_magic_states(&self, budget: &ErrorBudget, index: usize) -> Result<u64, String> {
140 let budget = self.convert_budget(budget).map_err(|e| e.to_string())?;
141 let result = self
142 .num_magic_states_method
143 .call1((budget, index))
144 .map_err(|e| e.to_string())?;
145
146 result.extract().map_err(|e| e.to_string())
147 }
148
149 fn prune_error_budget(
150 &self,
151 budget: &mut ErrorBudget,
152 strategy: ErrorBudgetStrategy,
153 ) -> Result<(), String> {
154 if let Some(method) = &self.prune_error_budget {
155 let budget_dict = self.convert_budget(budget).map_err(|e| e.to_string())?;
156 let strategy = Self::convert_error_budget_strategy(strategy);
157
158 method
159 .call1((budget_dict.clone(), strategy))
160 .map_err(|e| e.to_string())?;
161
162 budget.set_logical(
163 budget_dict
164 .get_item("logical")
165 .map_err(|e| e.to_string())?
166 .extract()
167 .map_err(|e| e.to_string())?,
168 );
169 budget.set_rotations(
170 budget_dict
171 .get_item("rotations")
172 .map_err(|e| e.to_string())?
173 .extract()
174 .map_err(|e| e.to_string())?,
175 );
176 budget.set_magic_states(
177 budget_dict
178 .get_item("magic_states")
179 .map_err(|e| e.to_string())?
180 .extract()
181 .map_err(|e| e.to_string())?,
182 );
183 }
184 Ok(())
185 }
186}
187