microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
jan

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/pip/src/generic_estimator/code.rs

185lines · modeblame

4dc9b0a0Mathias Soeken1 years ago1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use pyo3::{
5Bound, PyAny, PyResult,
e938e457orpuente-MS1 years ago6types::{PyAnyMethods, PyDict},
4dc9b0a0Mathias Soeken1 years ago7};
8use resource_estimator::estimates::ErrorCorrection;
9
e938e457orpuente-MS1 years ago10use super::utils::{SerializableBound, extract_and_check_method, maybe_extract_and_check_method};
4dc9b0a0Mathias Soeken1 years ago11
12/// A wrapper around a Python instance to compute quantum error correction
13/// properties in generic resource estimation.
14///
15/// The code parameter (e.g., distance) is generic and the possible code
16/// parameter values are returned via the `code_parameter_range` method and
17/// compared (for sorting) via the `code_parameter_cmp` method:
18///
19/// ```python
20/// def code_parameter_range(self):
21/// # e.g., return [7, 9, 11]
22/// ...
23///
24/// def code_parameter_cmp(self, qubit, p1, p2):
25/// # qubit properties (via qubit) may be considered for comparison
26/// #
27/// # must return -1 if p1 < p2, 0 if p1 == p2, and 1 if p1 > p2
28/// ...
29/// ```
30///
31/// The number of physical and logical qubits for one code patch depends on the
32/// code parameter and is computed with the methods `physical_qubits` and
33/// `logical_qubits`, respectively:
34///
35/// ```python
36/// def physical_qubits(self, param):
37/// # must return an int
38/// ...
39///
40/// def logical_qubits(self, param):
41/// # must return an int
42/// ...
43/// ```
44///
45/// Finally, the logical cycle time and logical error rate depend on qubit
46/// properties and the code parameter. They are computed using the methods
47/// `logical_cycle_time` and `logical_error_rate`, respectively, in which the
48/// qubit is a Python dictionary.
49///
50/// ```python
51/// def logical_cycle_time(self, qubit, param):
52/// # returns logical cycle time in nano seconds (int)
53/// ...
54///
55/// def logical_error_rate(self, qubit, param):
56/// # must return a float
57/// ...
58/// ```
59pub struct PythonQEC<'py> {
60qec: Bound<'py, PyAny>,
61physical_qubits_method: Bound<'py, PyAny>,
62logical_qubits_method: Bound<'py, PyAny>,
63logical_cycle_time_method: Bound<'py, PyAny>,
64logical_error_rate_method: Bound<'py, PyAny>,
65code_parameter_cmp_method: Bound<'py, PyAny>,
66adjust_code_parameter_method: Option<Bound<'py, PyAny>>,
67params: Vec<SerializableBound<'py>>,
68}
69
70impl<'py> PythonQEC<'py> {
71pub fn from_bound(qec: Bound<'py, PyAny>) -> PyResult<Self> {
72let physical_qubits_method = extract_and_check_method(&qec, "physical_qubits")?;
73let logical_qubits_method = extract_and_check_method(&qec, "logical_qubits")?;
74let logical_cycle_time_method = extract_and_check_method(&qec, "logical_cycle_time")?;
75let logical_error_rate_method = extract_and_check_method(&qec, "logical_error_rate")?;
76let code_parameter_range_method = extract_and_check_method(&qec, "code_parameter_range")?;
77let code_parameter_cmp_method = extract_and_check_method(&qec, "code_parameter_cmp")?;
78
79let adjust_code_parameter_method =
80maybe_extract_and_check_method(&qec, "adjust_code_parameter")?;
81
82let params0: Vec<Bound<'py, PyAny>> = code_parameter_range_method.call0()?.extract()?;
83
84let params: Vec<_> = params0.into_iter().map(SerializableBound).collect();
85Ok(Self {
86qec,
87physical_qubits_method,
88logical_qubits_method,
89logical_cycle_time_method,
90logical_error_rate_method,
91code_parameter_cmp_method,
92adjust_code_parameter_method,
93params,
94})
95}
96
97pub fn bound(&self) -> &Bound<'py, PyAny> {
98&self.qec
99}
100}
101
102impl<'py> ErrorCorrection for PythonQEC<'py> {
103type Qubit = Bound<'py, PyDict>;
104
105type Parameter = SerializableBound<'py>;
106
107fn physical_qubits(&self, code_parameter: &Self::Parameter) -> Result<u64, String> {
108let result = self
109.physical_qubits_method
110.call1((&**code_parameter,))
111.map_err(|e| e.to_string())?;
112
113result.extract().map_err(|e| e.to_string())
114}
115
116fn logical_qubits(&self, code_parameter: &Self::Parameter) -> Result<u64, String> {
117let result = self
118.logical_qubits_method
119.call1((&**code_parameter,))
120.map_err(|e| e.to_string())?;
121
122result.extract().map_err(|e| e.to_string())
123}
124
125fn logical_cycle_time(
126&self,
127qubit: &Self::Qubit,
128code_parameter: &Self::Parameter,
129) -> Result<u64, String> {
130let result = self
131.logical_cycle_time_method
132.call1((qubit, &**code_parameter))
133.map_err(|e| e.to_string())?;
134
135result.extract().map_err(|e| e.to_string())
136}
137
138fn logical_error_rate(
139&self,
140qubit: &Self::Qubit,
141code_parameter: &Self::Parameter,
142) -> Result<f64, String> {
143let result = self
144.logical_error_rate_method
145.call1((qubit, &**code_parameter))
146.map_err(|e| e.to_string())?;
147
148result.extract().map_err(|e| e.to_string())
149}
150
151fn code_parameter_range(
152&self,
153_lower_bound: Option<&Self::Parameter>,
154) -> impl Iterator<Item = Self::Parameter> {
155self.params.iter().cloned()
156}
157
158fn code_parameter_cmp(
159&self,
160qubit: &Self::Qubit,
161p1: &Self::Parameter,
162p2: &Self::Parameter,
163) -> std::cmp::Ordering {
164let result: i32 = self
165.code_parameter_cmp_method
166.call1((qubit, &**p1, &**p2))
167.expect("can call code_parameter_cmp method")
168.extract()
169.expect("can convert code_parameter_cmp return value into i32");
170
171result.cmp(&0)
172}
173
174fn adjust_code_parameter(&self, parameter: Self::Parameter) -> Result<Self::Parameter, String> {
175if let Some(method) = &self.adjust_code_parameter_method {
176let result = method.call1((&*parameter,)).map_err(|e| e.to_string())?;
177result
178.extract()
179.map(SerializableBound)
180.map_err(|e| e.to_string())
181} else {
182Ok(parameter)
183}
184}
185}