microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
7421e7dd1015dcbd940bf843d33583470de580ea

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/pip/src/generic_estimator/code.rs

185lines · 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::ErrorCorrection;
9
10use super::utils::{SerializableBound, extract_and_check_method, maybe_extract_and_check_method};
11
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> {
60 qec: Bound<'py, PyAny>,
61 physical_qubits_method: Bound<'py, PyAny>,
62 logical_qubits_method: Bound<'py, PyAny>,
63 logical_cycle_time_method: Bound<'py, PyAny>,
64 logical_error_rate_method: Bound<'py, PyAny>,
65 code_parameter_cmp_method: Bound<'py, PyAny>,
66 adjust_code_parameter_method: Option<Bound<'py, PyAny>>,
67 params: Vec<SerializableBound<'py>>,
68}
69
70impl<'py> PythonQEC<'py> {
71 pub fn from_bound(qec: Bound<'py, PyAny>) -> PyResult<Self> {
72 let physical_qubits_method = extract_and_check_method(&qec, "physical_qubits")?;
73 let logical_qubits_method = extract_and_check_method(&qec, "logical_qubits")?;
74 let logical_cycle_time_method = extract_and_check_method(&qec, "logical_cycle_time")?;
75 let logical_error_rate_method = extract_and_check_method(&qec, "logical_error_rate")?;
76 let code_parameter_range_method = extract_and_check_method(&qec, "code_parameter_range")?;
77 let code_parameter_cmp_method = extract_and_check_method(&qec, "code_parameter_cmp")?;
78
79 let adjust_code_parameter_method =
80 maybe_extract_and_check_method(&qec, "adjust_code_parameter")?;
81
82 let params0: Vec<Bound<'py, PyAny>> = code_parameter_range_method.call0()?.extract()?;
83
84 let params: Vec<_> = params0.into_iter().map(SerializableBound).collect();
85 Ok(Self {
86 qec,
87 physical_qubits_method,
88 logical_qubits_method,
89 logical_cycle_time_method,
90 logical_error_rate_method,
91 code_parameter_cmp_method,
92 adjust_code_parameter_method,
93 params,
94 })
95 }
96
97 pub fn bound(&self) -> &Bound<'py, PyAny> {
98 &self.qec
99 }
100}
101
102impl<'py> ErrorCorrection for PythonQEC<'py> {
103 type Qubit = Bound<'py, PyDict>;
104
105 type Parameter = SerializableBound<'py>;
106
107 fn physical_qubits(&self, code_parameter: &Self::Parameter) -> Result<u64, String> {
108 let result = self
109 .physical_qubits_method
110 .call1((&**code_parameter,))
111 .map_err(|e| e.to_string())?;
112
113 result.extract().map_err(|e| e.to_string())
114 }
115
116 fn logical_qubits(&self, code_parameter: &Self::Parameter) -> Result<u64, String> {
117 let result = self
118 .logical_qubits_method
119 .call1((&**code_parameter,))
120 .map_err(|e| e.to_string())?;
121
122 result.extract().map_err(|e| e.to_string())
123 }
124
125 fn logical_cycle_time(
126 &self,
127 qubit: &Self::Qubit,
128 code_parameter: &Self::Parameter,
129 ) -> Result<u64, String> {
130 let result = self
131 .logical_cycle_time_method
132 .call1((qubit, &**code_parameter))
133 .map_err(|e| e.to_string())?;
134
135 result.extract().map_err(|e| e.to_string())
136 }
137
138 fn logical_error_rate(
139 &self,
140 qubit: &Self::Qubit,
141 code_parameter: &Self::Parameter,
142 ) -> Result<f64, String> {
143 let result = self
144 .logical_error_rate_method
145 .call1((qubit, &**code_parameter))
146 .map_err(|e| e.to_string())?;
147
148 result.extract().map_err(|e| e.to_string())
149 }
150
151 fn code_parameter_range(
152 &self,
153 _lower_bound: Option<&Self::Parameter>,
154 ) -> impl Iterator<Item = Self::Parameter> {
155 self.params.iter().cloned()
156 }
157
158 fn code_parameter_cmp(
159 &self,
160 qubit: &Self::Qubit,
161 p1: &Self::Parameter,
162 p2: &Self::Parameter,
163 ) -> std::cmp::Ordering {
164 let 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
171 result.cmp(&0)
172 }
173
174 fn adjust_code_parameter(&self, parameter: Self::Parameter) -> Result<Self::Parameter, String> {
175 if let Some(method) = &self.adjust_code_parameter_method {
176 let result = method.call1((&*parameter,)).map_err(|e| e.to_string())?;
177 result
178 .extract()
179 .map(SerializableBound)
180 .map_err(|e| e.to_string())
181 } else {
182 Ok(parameter)
183 }
184 }
185}
186