microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
source/resource_estimator/src/system/data/result.rs
285lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | use std::ops::Deref; |
| 5 | use std::rc::Rc; |
| 6 | |
| 7 | use crate::estimates::{ErrorBudget, FactoryPart, LogicalPatch, PhysicalResourceEstimationResult}; |
| 8 | use crate::system::modeling::{Protocol, TFactory}; |
| 9 | |
| 10 | use super::LayoutReportData; |
| 11 | use super::{ |
| 12 | super::Error, FormattedPhysicalResourceCounts, JobParams, PhysicalResourceCounts, |
| 13 | PhysicalResourceCountsBreakdown, Report, |
| 14 | }; |
| 15 | use miette::Diagnostic; |
| 16 | use serde::{Serialize, Serializer, ser::SerializeMap}; |
| 17 | |
| 18 | #[derive(Serialize)] |
| 19 | #[serde(rename_all(serialize = "camelCase"))] |
| 20 | pub struct Success<L: LayoutReportData + Serialize> { |
| 21 | status: &'static str, |
| 22 | job_params: JobParams, |
| 23 | #[serde(skip_serializing_if = "Option::is_none")] |
| 24 | physical_counts: Option<PhysicalResourceCounts>, |
| 25 | #[serde(skip_serializing_if = "Option::is_none")] |
| 26 | physical_counts_formatted: Option<FormattedPhysicalResourceCounts>, |
| 27 | #[serde(skip_serializing_if = "Option::is_none")] |
| 28 | logical_qubit: Option<LogicalQubit>, |
| 29 | #[serde(skip_serializing_if = "Option::is_none")] |
| 30 | tfactory: Option<TFactory>, |
| 31 | #[serde(skip_serializing_if = "Option::is_none")] |
| 32 | error_budget: Option<ErrorBudget>, |
| 33 | logical_counts: Rc<L>, |
| 34 | report_data: Report, |
| 35 | #[serde(skip_serializing_if = "Vec::is_empty")] |
| 36 | frontier_entries: Vec<FrontierEntry>, |
| 37 | } |
| 38 | |
| 39 | impl<L: LayoutReportData + Serialize> Success<L> { |
| 40 | pub fn new( |
| 41 | job_params: JobParams, |
| 42 | layout_report_data: Rc<L>, |
| 43 | result: PhysicalResourceEstimationResult<Protocol, TFactory>, |
| 44 | ) -> Self { |
| 45 | let counts = create_physical_resource_counts(&result, layout_report_data.as_ref()); |
| 46 | |
| 47 | let formatted_counts: FormattedPhysicalResourceCounts = |
| 48 | FormattedPhysicalResourceCounts::new(&result, &job_params, layout_report_data.as_ref()); |
| 49 | |
| 50 | let report_data = Report::new( |
| 51 | &job_params, |
| 52 | layout_report_data.as_ref(), |
| 53 | &result, |
| 54 | &formatted_counts, |
| 55 | ); |
| 56 | |
| 57 | let (logical_qubit, mut parts, error_budget) = result.take(); |
| 58 | let tfactory = parts.swap_remove(0).map(FactoryPart::into_factory); |
| 59 | |
| 60 | Self { |
| 61 | status: "success", |
| 62 | job_params, |
| 63 | physical_counts: Some(counts), |
| 64 | physical_counts_formatted: Some(formatted_counts), |
| 65 | logical_qubit: Some(LogicalQubit(logical_qubit)), |
| 66 | tfactory, |
| 67 | error_budget: Some(error_budget), |
| 68 | logical_counts: layout_report_data, |
| 69 | report_data, |
| 70 | frontier_entries: Vec::new(), |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | pub fn new_from_multiple( |
| 75 | job_params: JobParams, |
| 76 | layout_report_data: Rc<L>, |
| 77 | mut results: Vec<PhysicalResourceEstimationResult<Protocol, TFactory>>, |
| 78 | ) -> Self { |
| 79 | let mut report_data: Option<Report> = None; |
| 80 | |
| 81 | let mut frontier_entries: Vec<FrontierEntry> = Vec::new(); |
| 82 | |
| 83 | // we will pick the shortest runtime result as the first result. |
| 84 | results.sort_by_key(PhysicalResourceEstimationResult::runtime); |
| 85 | for result in results { |
| 86 | let (frontier_entry, report) = create_frontier_entry( |
| 87 | &job_params, |
| 88 | result, |
| 89 | layout_report_data.as_ref(), |
| 90 | report_data.is_none(), |
| 91 | ); |
| 92 | |
| 93 | if report_data.is_none() { |
| 94 | report_data = Some(report.expect("error should have report")); |
| 95 | } |
| 96 | |
| 97 | frontier_entries.push(frontier_entry); |
| 98 | } |
| 99 | |
| 100 | Self { |
| 101 | status: "success", |
| 102 | job_params, |
| 103 | physical_counts: None, |
| 104 | physical_counts_formatted: None, |
| 105 | logical_qubit: None, |
| 106 | tfactory: None, |
| 107 | error_budget: None, |
| 108 | logical_counts: layout_report_data, |
| 109 | report_data: report_data.expect("error should have report"), // Here we assume that at least a single solution was found. |
| 110 | frontier_entries, |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | #[derive(Serialize)] |
| 116 | #[serde(rename_all(serialize = "camelCase"))] |
| 117 | pub struct FrontierEntry { |
| 118 | pub logical_qubit: LogicalQubit, |
| 119 | pub tfactory: Option<TFactory>, |
| 120 | pub error_budget: ErrorBudget, |
| 121 | pub physical_counts: PhysicalResourceCounts, |
| 122 | pub physical_counts_formatted: FormattedPhysicalResourceCounts, |
| 123 | } |
| 124 | |
| 125 | fn create_frontier_entry( |
| 126 | job_params: &JobParams, |
| 127 | result: PhysicalResourceEstimationResult<Protocol, TFactory>, |
| 128 | layout_report_data: &impl LayoutReportData, |
| 129 | create_report: bool, |
| 130 | ) -> (FrontierEntry, Option<Report>) { |
| 131 | let physical_counts = create_physical_resource_counts(&result, layout_report_data); |
| 132 | |
| 133 | let physical_counts_formatted: FormattedPhysicalResourceCounts = |
| 134 | FormattedPhysicalResourceCounts::new(&result, job_params, layout_report_data); |
| 135 | |
| 136 | let report_data = if create_report { |
| 137 | Some(Report::new( |
| 138 | job_params, |
| 139 | layout_report_data, |
| 140 | &result, |
| 141 | &physical_counts_formatted, |
| 142 | )) |
| 143 | } else { |
| 144 | None |
| 145 | }; |
| 146 | |
| 147 | let (logical_qubit, mut parts, error_budget) = result.take(); |
| 148 | let tfactory = parts.swap_remove(0).map(FactoryPart::into_factory); |
| 149 | |
| 150 | ( |
| 151 | FrontierEntry { |
| 152 | logical_qubit: LogicalQubit(logical_qubit), |
| 153 | tfactory, |
| 154 | error_budget, |
| 155 | physical_counts, |
| 156 | physical_counts_formatted, |
| 157 | }, |
| 158 | report_data, |
| 159 | ) |
| 160 | } |
| 161 | |
| 162 | fn create_physical_resource_counts( |
| 163 | result: &PhysicalResourceEstimationResult<Protocol, TFactory>, |
| 164 | layout_report_data: &impl LayoutReportData, |
| 165 | ) -> PhysicalResourceCounts { |
| 166 | let breakdown = create_physical_resource_counts_breakdown(result, layout_report_data); |
| 167 | |
| 168 | PhysicalResourceCounts { |
| 169 | physical_qubits: result.physical_qubits(), |
| 170 | runtime: result.runtime(), |
| 171 | rqops: result.rqops(), |
| 172 | breakdown, |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | fn create_physical_resource_counts_breakdown( |
| 177 | result: &PhysicalResourceEstimationResult<Protocol, TFactory>, |
| 178 | layout_report_data: &impl LayoutReportData, |
| 179 | ) -> PhysicalResourceCountsBreakdown { |
| 180 | let num_ts_per_rotation = |
| 181 | layout_report_data.num_ts_per_rotation(result.error_budget().rotations()); |
| 182 | |
| 183 | let part = result.factory_parts()[0].as_ref(); |
| 184 | |
| 185 | PhysicalResourceCountsBreakdown { |
| 186 | algorithmic_logical_qubits: result.layout_overhead().logical_qubits(), |
| 187 | algorithmic_logical_depth: result.layout_overhead().logical_depth(), |
| 188 | logical_depth: result.num_cycles(), |
| 189 | clock_frequency: result.logical_patch().logical_cycles_per_second(), |
| 190 | num_tstates: result.layout_overhead().num_magic_states()[0], |
| 191 | num_tfactories: part.map_or(0, FactoryPart::copies), |
| 192 | num_tfactory_runs: part.map_or(0, FactoryPart::runs), |
| 193 | physical_qubits_for_tfactories: result.physical_qubits_for_factories(), |
| 194 | physical_qubits_for_algorithm: result.physical_qubits_for_algorithm(), |
| 195 | required_logical_qubit_error_rate: result.required_logical_error_rate(), |
| 196 | required_logical_tstate_error_rate: part.map(FactoryPart::required_output_error_rate), |
| 197 | num_ts_per_rotation, |
| 198 | clifford_error_rate: result |
| 199 | .logical_patch() |
| 200 | .physical_qubit() |
| 201 | .clifford_error_rate(), |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | pub struct Failure { |
| 206 | error: Error, |
| 207 | batch_index: Option<usize>, |
| 208 | } |
| 209 | |
| 210 | impl Failure { |
| 211 | #[must_use] |
| 212 | pub fn new(error: Error) -> Self { |
| 213 | Self { |
| 214 | error, |
| 215 | batch_index: None, |
| 216 | } |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | impl Serialize for Failure { |
| 221 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| 222 | where |
| 223 | S: Serializer, |
| 224 | { |
| 225 | let mut map = serializer.serialize_map(Some(4))?; |
| 226 | |
| 227 | map.serialize_entry( |
| 228 | "code", |
| 229 | &self |
| 230 | .error |
| 231 | .code() |
| 232 | .expect("error should have code") |
| 233 | .to_string(), |
| 234 | )?; |
| 235 | if let Some(batch_index) = self.batch_index { |
| 236 | map.serialize_entry( |
| 237 | "message", |
| 238 | &format!("[batch index {}] {:?}", batch_index, self.error), |
| 239 | )?; |
| 240 | } else { |
| 241 | map.serialize_entry("message", &self.error.to_string())?; |
| 242 | } |
| 243 | |
| 244 | map.end() |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | /// A helper newtype to specialize serialization for `LogicalPatch<Protocol>` |
| 249 | pub struct LogicalQubit(LogicalPatch<Protocol>); |
| 250 | |
| 251 | impl Deref for LogicalQubit { |
| 252 | type Target = LogicalPatch<Protocol>; |
| 253 | |
| 254 | fn deref(&self) -> &Self::Target { |
| 255 | &self.0 |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | impl Serialize for LogicalQubit { |
| 260 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| 261 | where |
| 262 | S: Serializer, |
| 263 | { |
| 264 | let mut map = serializer.serialize_map(Some(4))?; |
| 265 | map.serialize_entry("codeDistance", &self.code_parameter())?; |
| 266 | map.serialize_entry("physicalQubits", &self.physical_qubits())?; |
| 267 | map.serialize_entry("logicalCycleTime", &self.logical_cycle_time())?; |
| 268 | map.serialize_entry("logicalErrorRate", &self.logical_error_rate())?; |
| 269 | |
| 270 | map.end() |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | impl Serialize for ErrorBudget { |
| 275 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| 276 | where |
| 277 | S: Serializer, |
| 278 | { |
| 279 | let mut map = serializer.serialize_map(Some(3))?; |
| 280 | map.serialize_entry("logical", &self.logical())?; |
| 281 | map.serialize_entry("tstates", &self.magic_states())?; |
| 282 | map.serialize_entry("rotations", &self.rotations())?; |
| 283 | map.end() |
| 284 | } |
| 285 | } |
| 286 | |