microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/openqasm-extern-compilation

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/resource_estimator/src/system/data/report.rs

652lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod tests;
6
7use serde::Serialize;
8
9use crate::estimates::{Factory, FactoryPart, PhysicalResourceEstimationResult};
10use crate::system::modeling::Protocol;
11
12use super::LayoutReportData;
13use super::{
14 super::modeling::{PhysicalInstructionSet, TFactory},
15 job_params::JobParams,
16};
17
18#[derive(Serialize)]
19#[serde(rename_all(serialize = "camelCase"))]
20pub struct Report {
21 groups: Vec<ReportEntryGroup>,
22 assumptions: Vec<String>,
23}
24
25impl Report {
26 #[allow(clippy::vec_init_then_push, clippy::too_many_lines)]
27 pub fn new(
28 job_params: &JobParams,
29 layout_report_data: &impl LayoutReportData,
30 result: &PhysicalResourceEstimationResult<Protocol, TFactory>,
31 formatted_counts: &FormattedPhysicalResourceCounts,
32 ) -> Self {
33 let logical_counts = layout_report_data;
34 // In this system, we consider T as the only magic state type, therefore
35 // there is only one factory part in the result.
36 let part = result.factory_parts()[0].as_ref();
37 let factory = part.map(FactoryPart::factory);
38
39 // THIS CODE HAS BEEN AUTOMATICALLY GENERATED WITH resource_estimator/scripts/generate_report_code.py from docs/output_data.md
40 let mut groups = vec![];
41
42 let mut entries = vec![];
43 entries.push(ReportEntry::new("physicalCountsFormatted/runtime", "Runtime", r#"Total runtime"#, &format!(r#"This is a runtime estimate for the execution time of the algorithm. In general, the execution time corresponds to the duration of one logical cycle ({} nanosecs) multiplied by the {} logical cycles to run the algorithm. If however the duration of a single T factory (here: {} nanosecs) is larger than the algorithm runtime, we extend the number of logical cycles artificially in order to exceed the runtime of a single T factory."#, format_thousand_sep(&result.logical_patch().logical_cycle_time()), format_thousand_sep(&result.algorithmic_logical_depth()), format_thousand_sep(&factory.map_or(0, TFactory::duration)))));
44 entries.push(ReportEntry::new("physicalCountsFormatted/rqops", "rQOPS", r#"Reliable quantum operations per second"#, &format!(r#"The value is computed as the number of logical qubits after layout ({}) (with a logical error rate of {}) multiplied by the clock frequency ({}), which is the number of logical cycles per second."#, format_thousand_sep(&result.layout_overhead().logical_qubits()), formatted_counts.required_logical_qubit_error_rate, format_thousand_sep_f64(result.logical_patch().logical_cycles_per_second()))));
45 entries.push(ReportEntry::new("physicalCountsFormatted/physicalQubits", "Physical qubits", r#"Number of physical qubits"#, &format!(r#"This value represents the total number of physical qubits, which is the sum of {} physical qubits to implement the algorithm logic, and {} physical qubits to execute the T factories that are responsible to produce the T states that are consumed by the algorithm."#, format_thousand_sep(&result.physical_qubits_for_algorithm()), format_thousand_sep(&result.physical_qubits_for_factories()))));
46 groups.push(ReportEntryGroup {
47 title: "Physical resource estimates".into(),
48 always_visible: true,
49 entries,
50 });
51
52 let mut entries = vec![];
53 entries.push(ReportEntry::new("physicalCountsFormatted/algorithmicLogicalQubits", "Logical algorithmic qubits", r#"Number of logical qubits for the algorithm after layout"#, &format!(r#"Laying out the logical qubits in the presence of nearest-neighbor constraints requires additional logical qubits. In particular, to layout the $Q_{{\rm alg}} = {}$ logical qubits in the input algorithm, we require in total $2 \cdot Q_{{\rm alg}} + \lceil \sqrt{{8 \cdot Q_{{\rm alg}}}}\rceil + 1 = {}$ logical qubits."#, format_thousand_sep(&logical_counts.num_qubits()), format_thousand_sep(&result.layout_overhead().logical_qubits()))));
54 entries.push(ReportEntry::new("physicalCountsFormatted/algorithmicLogicalDepth", "Algorithmic depth", r#"Number of logical cycles for the algorithm"#, &format!(r#"To execute the algorithm using _Parallel Synthesis Sequential Pauli Computation_ (PSSPC), operations are scheduled in terms of multi-qubit Pauli measurements, for which assume an execution time of one logical cycle. Based on the input algorithm, we require one multi-qubit measurement for the {} single-qubit measurements, the {} arbitrary single-qubit rotations, and the {} T gates, three multi-qubit measurements for each of the {} CCZ and {} CCiX gates in the input program, as well as {} multi-qubit measurements for each of the {} non-Clifford layers in which there is at least one single-qubit rotation with an arbitrary angle rotation."#, format_thousand_sep(&logical_counts.measurement_count()), format_thousand_sep(&logical_counts.rotation_count()), format_thousand_sep(&logical_counts.t_count()), format_thousand_sep(&logical_counts.ccz_count()), format_thousand_sep(&logical_counts.ccix_count()), formatted_counts.num_ts_per_rotation, format_thousand_sep(&logical_counts.rotation_depth()))));
55 entries.push(ReportEntry::new("physicalCountsFormatted/logicalDepth", "Logical depth", r#"Number of logical cycles performed"#, &format!(r#"This number is usually equal to the logical depth of the algorithm, which is {}. However, in the case in which a single T factory is slower than the execution time of the algorithm, we adjust the logical cycle depth to exceed the T factory's execution time."#, format_thousand_sep(&result.algorithmic_logical_depth()))));
56 entries.push(ReportEntry::new("physicalCountsFormatted/clockFrequency", "Clock frequency", r#"Number of logical cycles per second"#, &format!(r#"This is the number of logical cycles that can be performed within one second. The logical cycle time is {}."#, formatted_counts.logical_cycle_time)));
57 entries.push(ReportEntry::new("physicalCountsFormatted/numTstates", "Number of T states", r#"Number of T states consumed by the algorithm"#, &format!(r#"To execute the algorithm, we require one T state for each of the {} T gates, four T states for each of the {} CCZ and {} CCiX gates, as well as {} for each of the {} single-qubit rotation gates with arbitrary angle rotation."#, format_thousand_sep(&logical_counts.t_count()), format_thousand_sep(&logical_counts.ccz_count()), format_thousand_sep(&logical_counts.ccix_count()), formatted_counts.num_ts_per_rotation, format_thousand_sep(&logical_counts.rotation_count()))));
58 entries.push(ReportEntry::new("physicalCountsFormatted/numTfactories", "Number of T factories", &format!(r#"Number of T factories capable of producing the demanded {} T states during the algorithm's runtime"#, format_thousand_sep(&result.num_magic_states(0))), &format!(r#"The total number of T factories {} that are executed in parallel is computed as $\left\lceil\dfrac{{\text{{T states}}\cdot\text{{T factory duration}}}}{{\text{{T states per T factory}}\cdot\text{{algorithm runtime}}}}\right\rceil = \left\lceil\dfrac{{{} \cdot {}\;\text{{ns}}}}{{{} \cdot {}\;\text{{ns}}}}\right\rceil$"#, format_thousand_sep(&part.map_or(0, FactoryPart::copies)), format_thousand_sep(&result.num_magic_states(0)), format_thousand_sep(&factory.map_or(0, TFactory::duration)), format_thousand_sep(&factory.map_or(0, TFactory::num_output_states)), format_thousand_sep(&result.runtime()))));
59 entries.push(ReportEntry::new("physicalCountsFormatted/numTfactoryRuns", "Number of T factory invocations", r#"Number of times all T factories are invoked"#, &format!(r#"In order to prepare the {} T states, the {} copies of the T factory are repeatedly invoked {} times."#, format_thousand_sep(&result.num_magic_states(0)), format_thousand_sep(&part.map_or(0, FactoryPart::copies)), format_thousand_sep(&part.map_or(0, FactoryPart::runs)))));
60 entries.push(ReportEntry::new("physicalCountsFormatted/physicalQubitsForAlgorithm", "Physical algorithmic qubits", r#"Number of physical qubits for the algorithm after layout"#, &format!(r#"The {} are the product of the {} logical qubits after layout and the {} physical qubits that encode a single logical qubit."#, format_thousand_sep(&result.physical_qubits_for_algorithm()), format_thousand_sep(&result.layout_overhead().logical_qubits()), format_thousand_sep(&result.logical_patch().physical_qubits()))));
61 entries.push(ReportEntry::new("physicalCountsFormatted/physicalQubitsForTfactories", "Physical T factory qubits", r#"Number of physical qubits for the T factories"#, &format!(r#"Each T factory requires {} physical qubits and we run {} in parallel, therefore we need ${} = {} \cdot {}$ qubits."#, format_thousand_sep(&factory.map_or(0, TFactory::physical_qubits)), format_thousand_sep(&part.map_or(0, FactoryPart::copies)), format_thousand_sep(&result.physical_qubits_for_factories()), format_thousand_sep(&factory.map_or(0, TFactory::physical_qubits)), format_thousand_sep(&part.map_or(0, FactoryPart::copies)))));
62 entries.push(ReportEntry::new("physicalCountsFormatted/requiredLogicalQubitErrorRate", "Required logical qubit error rate", r#"The minimum logical qubit error rate required to run the algorithm within the error budget"#, &format!(r#"The minimum logical qubit error rate is obtained by dividing the logical error probability {} by the product of {} logical qubits and the total cycle count {}."#, formatted_counts.error_budget_logical, format_thousand_sep(&result.layout_overhead().logical_qubits()), format_thousand_sep(&result.num_cycles()))));
63 entries.push(ReportEntry::new("physicalCountsFormatted/requiredLogicalTstateErrorRate", "Required logical T state error rate", r#"The minimum T state error rate required for distilled T states"#, &format!(r#"The minimum T state error rate is obtained by dividing the T distillation error probability {} by the total number of T states {}."#, formatted_counts.error_budget_tstates, format_thousand_sep(&result.num_magic_states(0)))));
64 entries.push(ReportEntry::new("physicalCountsFormatted/numTsPerRotation", "Number of T states per rotation", r#"Number of T states to implement a rotation with an arbitrary angle"#, &format!(r#"The number of T states to implement a rotation with an arbitrary angle is $\lceil 0.53 \log_2({} / {}) + 4.86\rceil$ [[arXiv:2203.10064](https://arxiv.org/abs/2203.10064)]. For simplicity, we use this formula for all single-qubit arbitrary angle rotations, and do not distinguish between best, worst, and average cases."#, format_thousand_sep(&logical_counts.rotation_count()), result.error_budget().rotations())));
65 groups.push(ReportEntryGroup {
66 title: "Resource estimates breakdown".into(),
67 always_visible: false,
68 entries,
69 });
70
71 let mut entries = vec![];
72 entries.push(ReportEntry::new("jobParams/qecScheme/name", "QEC scheme", r#"Name of QEC scheme"#, r#"You can load pre-defined QEC schemes by using the name `surface_code` or `floquet_code`. The latter only works with Majorana qubits."#));
73 entries.push(ReportEntry::new("logicalQubit/codeDistance", "Code distance", r#"Required code distance for error correction"#, &format!(r#"The code distance is the smallest odd integer greater or equal to $\dfrac{{2\log({} / {})}}{{\log({}/{})}} - 1$"#, job_params.qec_scheme().crossing_prefactor.expect("crossing prefactor should be set"), result.required_logical_error_rate(), job_params.qec_scheme().error_correction_threshold.expect("error correction threshold should be set"), result.logical_patch().physical_qubit().clifford_error_rate())));
74 entries.push(ReportEntry::new("physicalCountsFormatted/physicalQubitsPerLogicalQubit", "Physical qubits", r#"Number of physical qubits per logical qubit"#, &format!(r#"The number of physical qubits per logical qubit are evaluated using the formula {} that can be user-specified."#, job_params.qec_scheme().physical_qubits_per_logical_qubit.as_ref().expect("physical qubits per logical qubit should be set"))));
75 entries.push(ReportEntry::new("physicalCountsFormatted/logicalCycleTime", "Logical cycle time", r#"Duration of a logical cycle in nanoseconds"#, &format!(r#"The runtime of one logical cycle in nanoseconds is evaluated using the formula {} that can be user-specified."#, job_params.qec_scheme().logical_cycle_time.as_ref().expect("logical cycle time should be set"))));
76 entries.push(ReportEntry::new("physicalCountsFormatted/logicalErrorRate", "Logical qubit error rate", r#"Logical qubit error rate"#, &format!(r#"The logical qubit error rate is computed as ${} \cdot \left(\dfrac{{{}}}{{{}}}\right)^\frac{{{} + 1}}{{2}}$"#, job_params.qec_scheme().crossing_prefactor.expect("crossing prefactor should be set"), result.logical_patch().physical_qubit().clifford_error_rate(), job_params.qec_scheme().error_correction_threshold.expect("error correction threshold should be set"), result.logical_patch().code_parameter())));
77 entries.push(ReportEntry::new("jobParams/qecScheme/crossingPrefactor", "Crossing prefactor", r#"Crossing prefactor used in QEC scheme"#, r#"The crossing prefactor is usually extracted numerically from simulations when fitting an exponential curve to model the relationship between logical and physical error rate."#));
78 entries.push(ReportEntry::new("jobParams/qecScheme/errorCorrectionThreshold", "Error correction threshold", r#"Error correction threshold used in QEC scheme"#, r#"The error correction threshold is the physical error rate below which the error rate of the logical qubit is less than the error rate of the physical qubit that constitute it. This value is usually extracted numerically from simulations of the logical error rate."#));
79 entries.push(ReportEntry::new(
80 "jobParams/qecScheme/logicalCycleTime",
81 "Logical cycle time formula",
82 r#"QEC scheme formula used to compute logical cycle time"#,
83 &format!(
84 r#"This is the formula that is used to compute the logical cycle time {} ns."#,
85 format_thousand_sep(&result.logical_patch().logical_cycle_time())
86 ),
87 ));
88 entries.push(ReportEntry::new("jobParams/qecScheme/physicalQubitsPerLogicalQubit", "Physical qubits formula", r#"QEC scheme formula used to compute number of physical qubits per logical qubit"#, &format!(r#"This is the formula that is used to compute the number of physical qubits per logical qubits {}."#, format_thousand_sep(&result.logical_patch().physical_qubits()))));
89 groups.push(ReportEntryGroup {
90 title: "Logical qubit parameters".into(),
91 always_visible: false,
92 entries,
93 });
94
95 if let Some(part) = part {
96 let mut entries = vec![];
97 entries.push(ReportEntry::new("physicalCountsFormatted/tfactoryPhysicalQubits", "Physical qubits", r#"Number of physical qubits for a single T factory"#, r#"This corresponds to the maximum number of physical qubits over all rounds of T distillation units in a T factory. A round of distillation contains of multiple copies of distillation units to achieve the required success probability of producing a T state with the expected logical T state error rate."#));
98 entries.push(ReportEntry::new("physicalCountsFormatted/tfactoryRuntime", "Runtime", r#"Runtime of a single T factory"#, r#"The runtime of a single T factory is the accumulated runtime of executing each round in a T factory."#));
99 entries.push(ReportEntry::new("tfactory/numTstates", "Number of output T states per run", r#"Number of output T states produced in a single run of T factory"#, &format!(r#"The T factory takes as input {} noisy physical T states with an error rate of {} and produces {} T states with an error rate of {}."#, format_thousand_sep(&part.factory().num_input_states()), job_params.qubit_params().t_gate_error_rate(), format_thousand_sep(&part.factory().num_output_states()), formatted_counts.tstate_logical_error_rate)));
100 entries.push(ReportEntry::new("physicalCountsFormatted/numInputTstates", "Number of input T states per run", r#"Number of physical input T states consumed in a single run of a T factory"#, r#"This value includes the physical input T states of all copies of the distillation unit in the first round."#));
101 entries.push(ReportEntry::new("tfactory/numRounds", "Distillation rounds", r#"The number of distillation rounds"#, r#"This is the number of distillation rounds. In each round one or multiple copies of some distillation unit is executed."#));
102 entries.push(ReportEntry::new(
103 "physicalCountsFormatted/numUnitsPerRound",
104 "Distillation units per round",
105 r#"The number of units in each round of distillation"#,
106 r#"This is the number of copies for the distillation units per round."#,
107 ));
108 entries.push(ReportEntry::new("physicalCountsFormatted/unitNamePerRound", "Distillation units", r#"The types of distillation units"#, r#"These are the types of distillation units that are executed in each round. The units can be either physical or logical, depending on what type of qubit they are operating. Space-efficient units require fewer qubits for the cost of longer runtime compared to Reed-Muller preparation units."#));
109 entries.push(ReportEntry::new("physicalCountsFormatted/codeDistancePerRound", "Distillation code distances", r#"The code distance in each round of distillation"#, r#"This is the code distance used for the units in each round. If the code distance is 1, then the distillation unit operates on physical qubits instead of error-corrected logical qubits."#));
110 entries.push(ReportEntry::new("physicalCountsFormatted/physicalQubitsPerRound", "Number of physical qubits per round", r#"The number of physical qubits used in each round of distillation"#, r#"The maximum number of physical qubits over all rounds is the number of physical qubits for the T factory, since qubits are reused by different rounds."#));
111 entries.push(ReportEntry::new(
112 "physicalCountsFormatted/tfactoryRuntimePerRound",
113 "Runtime per round",
114 r#"The runtime of each distillation round"#,
115 r#"The runtime of the T factory is the sum of the runtimes in all rounds."#,
116 ));
117 entries.push(ReportEntry::new("physicalCountsFormatted/tstateLogicalErrorRate", "Logical T state error rate", r#"Logical T state error rate"#, &format!(r#"This is the logical T state error rate achieved by the T factory which is equal or smaller than the required error rate {}."#, formatted_counts.required_logical_tstate_error_rate)));
118 groups.push(ReportEntryGroup {
119 title: "T factory parameters".into(),
120 always_visible: false,
121 entries,
122 });
123 }
124 let mut entries = vec![];
125 entries.push(ReportEntry::new("physicalCountsFormatted/logicalCountsNumQubits", "Logical qubits (pre-layout)", r#"Number of logical qubits in the input quantum program"#, &format!(r#"We determine {} algorithmic logical qubits from this number by assuming to align them in a 2D grid. Auxiliary qubits are added to allow for sufficient space to execute multi-qubit Pauli measurements on all or a subset of the logical qubits."#, format_thousand_sep(&result.layout_overhead().logical_qubits()))));
126 entries.push(ReportEntry::new("physicalCountsFormatted/logicalCountsTCount", "T gates", r#"Number of T gates in the input quantum program"#, r#"This includes all T gates and adjoint T gates, but not T gates used to implement rotation gates with arbitrary angle, CCZ gates, or CCiX gates."#));
127 entries.push(ReportEntry::new("physicalCountsFormatted/logicalCountsRotationCount", "Rotation gates", r#"Number of rotation gates in the input quantum program"#, r#"This is the number of all rotation gates. If an angle corresponds to a Pauli, Clifford, or T gate, it is not accounted for in this number."#));
128 entries.push(ReportEntry::new("physicalCountsFormatted/logicalCountsRotationDepth", "Rotation depth", r#"Depth of rotation gates in the input quantum program"#, r#"This is the number of all non-Clifford layers that include at least one single-qubit rotation gate with an arbitrary angle."#));
129 entries.push(ReportEntry::new(
130 "physicalCountsFormatted/logicalCountsCczCount",
131 "CCZ gates",
132 r#"Number of CCZ-gates in the input quantum program"#,
133 r#"This is the number of CCZ gates."#,
134 ));
135 entries.push(ReportEntry::new("physicalCountsFormatted/logicalCountsCcixCount", "CCiX gates", r#"Number of CCiX-gates in the input quantum program"#, r#"This is the number of CCiX gates, which applies $-iX$ controlled on two control qubits [[1212.5069](https://arxiv.org/abs/1212.5069)]."#));
136 entries.push(ReportEntry::new("physicalCountsFormatted/logicalCountsMeasurementCount", "Measurement operations", r#"Number of single qubit measurements in the input quantum program"#, r#"This is the number of single qubit measurements in Pauli basis that are used in the input program. Note that all measurements are counted, however, the measurement result is is determined randomly (with a fixed seed) to be 0 or 1 with a probability of 50%."#));
137 groups.push(ReportEntryGroup {
138 title: "Pre-layout logical resources".into(),
139 always_visible: false,
140 entries,
141 });
142
143 let mut entries = vec![];
144 entries.push(ReportEntry::new("physicalCountsFormatted/errorBudget", "Total error budget", r#"Total error budget for the algorithm"#, r"The total error budget sets the overall allowed error for the algorithm, i.e., the number of times it is allowed to fail. Its value must be between 0 and 1 and the default value is 0.001, which corresponds to 0.1%, and means that the algorithm is allowed to fail once in 1000 executions. This parameter is highly application specific. For example, if one is running Shor's algorithm for factoring integers, a large value for the error budget may be tolerated as one can check that the output are indeed the prime factors of the input. On the other hand, a much smaller error budget may be needed for an algorithm solving a problem with a solution which cannot be efficiently verified. This budget $\epsilon = \epsilon_{\log} + \epsilon_{\rm dis} + \epsilon_{\rm syn}$ is uniformly distributed and applies to errors $\epsilon_{\log}$ to implement logical qubits, an error budget $\epsilon_{\rm dis}$ to produce T states through distillation, and an error budget $\epsilon_{\rm syn}$ to synthesize rotation gates with arbitrary angles. Note that for distillation and rotation synthesis, the respective error budgets $\epsilon_{\rm dis}$ and $\epsilon_{\rm syn}$ are uniformly distributed among all T states and all rotation gates, respectively. If there are no rotation gates in the input algorithm, the error budget is uniformly distributed to logical errors and T state errors."));
145 entries.push(ReportEntry::new("physicalCountsFormatted/errorBudgetLogical", "Logical error probability", r#"Probability of at least one logical error"#, &format!(r#"This is one third of the total error budget {} if the input algorithm contains rotation with gates with arbitrary angles, or one half of it, otherwise."#, formatted_counts.error_budget)));
146 entries.push(ReportEntry::new("physicalCountsFormatted/errorBudgetTstates", "T distillation error probability", r#"Probability of at least one faulty T distillation"#, &format!(r#"This is one third of the total error budget {} if the input algorithm contains rotation with gates with arbitrary angles, or one half of it, otherwise."#, formatted_counts.error_budget)));
147 entries.push(ReportEntry::new(
148 "physicalCountsFormatted/errorBudgetRotations",
149 "Rotation synthesis error probability",
150 r#"Probability of at least one failed rotation synthesis"#,
151 &format!(
152 r#"This is one third of the total error budget {}."#,
153 formatted_counts.error_budget
154 ),
155 ));
156 groups.push(ReportEntryGroup {
157 title: "Assumed error budget".into(),
158 always_visible: false,
159 entries,
160 });
161
162 let mut entries = vec![];
163 entries.push(ReportEntry::new("jobParams/qubitParams/name", "Qubit name", r#"Some descriptive name for the qubit model"#, r#"You can load pre-defined qubit parameters by using the names `qubit_gate_ns_e3`, `qubit_gate_ns_e4`, `qubit_gate_us_e3`, `qubit_gate_us_e4`, `qubit_maj_ns_e4`, or `qubit_maj_ns_e6`. The names of these pre-defined qubit parameters indicate the instruction set (gate-based or Majorana), the operation speed (ns or µs regime), as well as the fidelity (e.g., e3 for $10^{-3}$ gate error rates)."#));
164 entries.push(ReportEntry::new("jobParams/qubitParams/instructionSet", "Instruction set", r#"Underlying qubit technology (gate-based or Majorana)"#, r#"When modeling the physical qubit abstractions, we distinguish between two different physical instruction sets that are used to operate the qubits. The physical instruction set can be either *gate-based* or *Majorana*. A gate-based instruction set provides single-qubit measurement, single-qubit gates (incl. T gates), and two-qubit gates. A Majorana instruction set provides a physical T gate, single-qubit measurement and two-qubit joint measurement operations."#));
165 entries.push(ReportEntry::new("jobParams/qubitParams/oneQubitMeasurementTime", "Single-qubit measurement time", r#"Operation time for single-qubit measurement (t_meas) in ns"#, r#"This is the operation time in nanoseconds to perform a single-qubit measurement in the Pauli basis."#));
166 if job_params.qubit_params().instruction_set() == PhysicalInstructionSet::Majorana {
167 entries.push(ReportEntry::new("jobParams/qubitParams/twoQubitJointMeasurementTime", "Two-qubit measurement time", r#"Operation time for two-qubit measurement in ns"#, r#"This is the operation time in nanoseconds to perform a non-destructive two-qubit joint Pauli measurement."#));
168 }
169 if job_params.qubit_params().instruction_set() == PhysicalInstructionSet::GateBased {
170 entries.push(ReportEntry::new("jobParams/qubitParams/oneQubitGateTime", "Single-qubit gate time", r#"Operation time for single-qubit gate (t_gate) in ns"#, r#"This is the operation time in nanoseconds to perform a single-qubit Clifford operation, e.g., Hadamard or Phase gates."#));
171 }
172 if job_params.qubit_params().instruction_set() == PhysicalInstructionSet::GateBased {
173 entries.push(ReportEntry::new("jobParams/qubitParams/twoQubitGateTime", "Two-qubit gate time", r#"Operation time for two-qubit gate in ns"#, r#"This is the operation time in nanoseconds to perform a two-qubit Clifford operation, e.g., a CNOT or CZ gate."#));
174 }
175 entries.push(ReportEntry::new(
176 "jobParams/qubitParams/tGateTime",
177 "T gate time",
178 r#"Operation time for a T gate"#,
179 r#"This is the operation time in nanoseconds to execute a T gate."#,
180 ));
181 entries.push(ReportEntry::new("jobParams/qubitParams/oneQubitMeasurementErrorRate", "Single-qubit measurement error rate", r#"Error rate for single-qubit measurement"#, r#"This is the probability in which a single-qubit measurement in the Pauli basis may fail."#));
182 if job_params.qubit_params().instruction_set() == PhysicalInstructionSet::Majorana {
183 entries.push(ReportEntry::new("jobParams/qubitParams/twoQubitJointMeasurementErrorRate", "Two-qubit measurement error rate", r#"Error rate for two-qubit measurement"#, r#"This is the probability in which a non-destructive two-qubit joint Pauli measurement may fail."#));
184 }
185 if job_params.qubit_params().instruction_set() == PhysicalInstructionSet::GateBased {
186 entries.push(ReportEntry::new("jobParams/qubitParams/oneQubitGateErrorRate", "Single-qubit error rate", r#"Error rate for single-qubit Clifford gate (p)"#, r#"This is the probability in which a single-qubit Clifford operation, e.g., Hadamard or Phase gates, may fail."#));
187 }
188 if job_params.qubit_params().instruction_set() == PhysicalInstructionSet::GateBased {
189 entries.push(ReportEntry::new("jobParams/qubitParams/twoQubitGateErrorRate", "Two-qubit error rate", r#"Error rate for two-qubit Clifford gate"#, r#"This is the probability in which a two-qubit Clifford operation, e.g., CNOT or CZ gates, may fail."#));
190 }
191 entries.push(ReportEntry::new(
192 "jobParams/qubitParams/tGateErrorRate",
193 "T gate error rate",
194 r#"Error rate to prepare single-qubit T state or apply a T gate (p_T)"#,
195 r#"This is the probability in which executing a single T gate may fail."#,
196 ));
197 groups.push(ReportEntryGroup {
198 title: "Physical qubit parameters".into(),
199 always_visible: false,
200 entries,
201 });
202
203 let mut entries = vec![];
204 entries.push(ReportEntry::new("physicalCountsFormatted/logicalDepthFactor", "Logical depth factor", r#"Factor the initial number of logical cycles is multiplied by"#, r#"This is the factor takes into account a potential overhead to the initial number of logical cycles."#));
205 entries.push(ReportEntry::new("physicalCountsFormatted/maxTFactories", "Maximum number of T factories", r#"The maximum number of T factories can be utilized during the algorithm's runtime"#, r#"This is the maximum number of T factories used for producing the demanded T states, which can be created and executed by the algorithm in parallel."#));
206 entries.push(ReportEntry::new("physicalCountsFormatted/maxDuration", "Maximum runtime duration", r#"The maximum runtime duration allowed for the algorithm runtime"#, r#"This is the maximum time allowed to the algorithm. If specified, the estimator targets to minimize the number of physical qubits consumed by the algorithm for runtimes under the maximum allowed."#));
207 entries.push(ReportEntry::new("physicalCountsFormatted/maxPhysicalQubits", "Maximum number of physical qubits", r#"The maximum number of physical qubits allowed for utilization to the algorith"#, r#"This is the maximum number of physical qubits available to the algorithm. If specified, the estimator targets to minimize the runtime of the algorithm with number of physical qubits consumed not exceeding this maximum."#));
208 groups.push(ReportEntryGroup {
209 title: "Constraints".into(),
210 always_visible: false,
211 entries,
212 });
213
214 let assumptions = vec![
215 String::from(
216 "_More details on the following lists of assumptions can be found in the paper [Accessing requirements for scaling quantum computers and their applications](https://aka.ms/AQ/RE/Paper)._",
217 ),
218 String::from(
219 "**Uniform independent physical noise.** We assume that the noise on physical qubits and physical qubit operations is the standard circuit noise model. In particular we assume error events at different space-time locations are independent and that error rates are uniform across the system in time and space.",
220 ),
221 String::from(
222 "**Efficient classical computation.** We assume that classical overhead (compilation, control, feedback, readout, decoding, etc.) does not dominate the overall cost of implementing the full quantum algorithm.",
223 ),
224 String::from(
225 "**Extraction circuits for planar quantum ISA.** We assume that stabilizer extraction circuits with similar depth and error correction performance to those for standard surface and Hastings-Haah code patches can be constructed to implement all operations of the planar quantum ISA (instruction set architecture).",
226 ),
227 String::from(
228 "**Uniform independent logical noise.** We assume that the error rate of a logical operation is approximately equal to its space-time volume (the number of tiles multiplied by the number of logical time steps) multiplied by the error rate of a logical qubit in a standard one-tile patch in one logical time step.",
229 ),
230 String::from(
231 "**Negligible Clifford costs for synthesis.** We assume that the space overhead for synthesis and space and time overhead for transport of magic states within magic state factories and to synthesis qubits are all negligible.",
232 ),
233 String::from(
234 "**Smooth magic state consumption rate.** We assume that the rate of T state consumption throughout the compiled algorithm is almost constant, or can be made almost constant without significantly increasing the number of logical time steps for the algorithm.",
235 ),
236 ];
237
238 Self {
239 groups,
240 assumptions,
241 }
242 }
243}
244
245#[derive(Serialize)]
246#[serde(rename_all(serialize = "camelCase"))]
247struct ReportEntryGroup {
248 title: String,
249 always_visible: bool,
250 entries: Vec<ReportEntry>,
251}
252
253#[derive(Serialize)]
254#[serde(rename_all(serialize = "camelCase"))]
255struct ReportEntry {
256 path: String,
257 label: String,
258 description: String,
259 explanation: String,
260}
261
262impl ReportEntry {
263 pub fn new(path: &str, label: &str, description: &str, explanation: &str) -> Self {
264 ReportEntry {
265 path: path.into(),
266 label: label.into(),
267 description: description.into(),
268 explanation: explanation.into(),
269 }
270 }
271}
272
273#[derive(Default, Debug, serde::Serialize)]
274#[serde(rename_all(serialize = "camelCase"))]
275pub struct FormattedPhysicalResourceCounts {
276 /// Total runtime as human friendly string
277 pub(crate) runtime: String,
278
279 /// Reliable QOPS formatted with metric prefix
280 pub(crate) rqops: String,
281
282 /// Total number of physical qubits 1000-separated
283 pub(crate) physical_qubits: String,
284
285 pub(crate) algorithmic_logical_qubits: String,
286 pub(crate) algorithmic_logical_depth: String,
287 pub(crate) logical_depth: String,
288 pub(crate) num_tstates: String,
289 pub(crate) num_tfactories: String,
290 pub(crate) num_tfactory_runs: String,
291 pub(crate) physical_qubits_for_algorithm: String,
292 pub(crate) physical_qubits_for_tfactories: String,
293
294 /// The number of physical qubits for all T-factories in percentage to total
295 pub(crate) physical_qubits_for_tfactories_percentage: String,
296
297 /// Truncated required logical qubit error rate
298 pub(crate) required_logical_qubit_error_rate: String,
299
300 /// Truncated required T-state error rate
301 pub(crate) required_logical_tstate_error_rate: String,
302
303 pub(crate) physical_qubits_per_logical_qubit: String,
304
305 /// The logical cycle time of a logical qubit as human friendly string
306 pub(crate) logical_cycle_time: String,
307
308 /// The number of logical cycles per second as a human friendly string
309 pub(crate) clock_frequency: String,
310
311 /// Truncated logical error rate
312 pub(crate) logical_error_rate: String,
313
314 pub(crate) tfactory_physical_qubits: String,
315
316 /// The runtime of a single T-factory as human friendly string
317 pub(crate) tfactory_runtime: String,
318
319 pub(crate) num_input_tstates: String,
320
321 /// The number of units per distillation round, comma separated in a string
322 pub(crate) num_units_per_round: String,
323
324 /// The unit names of each distallation round, comma separated in a string
325 pub(crate) unit_name_per_round: String,
326
327 /// The code distances per distillation round, comma separated in a string
328 pub(crate) code_distance_per_round: String,
329
330 /// The number of physical qubits per distillation round, comma separated in a string
331 pub(crate) physical_qubits_per_round: String,
332
333 /// The runtime of each distillation round, displayed as comma separated human friendly strings
334 pub(crate) tfactory_runtime_per_round: String,
335
336 /// Truncated logical T-state error rate
337 pub(crate) tstate_logical_error_rate: String,
338
339 pub(crate) logical_counts_num_qubits: String,
340 pub(crate) logical_counts_t_count: String,
341 pub(crate) logical_counts_rotation_count: String,
342 pub(crate) logical_counts_rotation_depth: String,
343 pub(crate) logical_counts_ccz_count: String,
344 pub(crate) logical_counts_ccix_count: String,
345 pub(crate) logical_counts_measurement_count: String,
346
347 /// Truncated total error budget
348 pub(crate) error_budget: String,
349
350 /// Truncated error budget for logical error
351 pub(crate) error_budget_logical: String,
352
353 /// Truncated error budget for faulty T state distillation
354 pub(crate) error_budget_tstates: String,
355
356 /// Truncated error budget for faulty rotation synthesis
357 pub(crate) error_budget_rotations: String,
358
359 /// Formatted number of Ts per rotation (might be None)
360 pub(crate) num_ts_per_rotation: String,
361
362 /// Formatted logical depth factor constraint
363 pub(crate) logical_depth_factor: String,
364 /// Formatted max T factories constraint
365 pub(crate) max_t_factories: String,
366 /// Formatted max duration constraint
367 pub(crate) max_duration: String,
368 /// Formatted max physical qubits constraint
369 pub(crate) max_physical_qubits: String,
370}
371
372impl FormattedPhysicalResourceCounts {
373 #[allow(clippy::too_many_lines)]
374 pub fn new(
375 result: &PhysicalResourceEstimationResult<Protocol, TFactory>,
376 job_params: &JobParams,
377 layout_report_data: &impl LayoutReportData,
378 ) -> Self {
379 // Physical resource estimates
380 let runtime = format_duration(result.runtime().into());
381 let rqops = format_metric_prefix(result.rqops());
382 let physical_qubits = format_metric_prefix(result.physical_qubits());
383 let part = result.factory_parts()[0].as_ref();
384 let factory = part.map(FactoryPart::factory);
385
386 // Resource estimates breakdown
387
388 let algorithmic_logical_qubits =
389 format_metric_prefix(result.layout_overhead().logical_qubits());
390 let algorithmic_logical_depth = format_metric_prefix(result.algorithmic_logical_depth());
391 let logical_depth = format_metric_prefix(result.num_cycles());
392 let num_tstates = format_metric_prefix(result.num_magic_states(0));
393 let num_tfactories = format_metric_prefix(part.map_or(0, FactoryPart::copies));
394 let num_tfactory_runs = format_metric_prefix(part.map_or(0, FactoryPart::runs));
395 let physical_qubits_for_algorithm =
396 format_metric_prefix(result.physical_qubits_for_algorithm());
397 let physical_qubits_for_tfactories =
398 format_metric_prefix(result.physical_qubits_for_factories());
399
400 let physical_qubits_for_tfactories_percentage = format!(
401 "{:.2} %",
402 (result.physical_qubits_for_factories() * 100) as f64 / result.physical_qubits() as f64
403 );
404
405 let required_logical_qubit_error_rate =
406 format!("{:.2e}", result.required_logical_error_rate());
407
408 let no_tstates_msg = "No T states in algorithm";
409 let no_rotations_msg = "No rotations in algorithm";
410
411 let required_logical_tstate_error_rate = part.map_or(String::from(no_tstates_msg), |p| {
412 format!("{:.2e}", p.required_output_error_rate())
413 });
414
415 // Logical qubit parameters
416 let physical_qubits_per_logical_qubit =
417 format_metric_prefix(result.logical_patch().physical_qubits());
418
419 let logical_cycle_time =
420 format_duration(result.logical_patch().logical_cycle_time() as u128);
421
422 let clock_frequency =
423 format_metric_prefix(result.logical_patch().logical_cycles_per_second().round() as u64);
424
425 let logical_error_rate = format!("{:.2e}", result.logical_patch().logical_error_rate());
426
427 // T factory parameters
428 let tfactory_physical_qubits = factory.map_or(String::from(no_tstates_msg), |tfactory| {
429 format_metric_prefix(tfactory.physical_qubits())
430 });
431 let tfactory_runtime = factory.map_or(String::from(no_tstates_msg), |tfactory| {
432 format_duration(tfactory.duration() as u128)
433 });
434 let num_input_tstates = factory.map_or(String::from(no_tstates_msg), |tfactory| {
435 format_metric_prefix(tfactory.num_input_states())
436 });
437
438 let num_units_per_round = factory.map_or(String::from(no_tstates_msg), |tfactory| {
439 tfactory
440 .num_units_per_round()
441 .iter()
442 .map(|&num| num.to_string())
443 .collect::<Vec<_>>()
444 .join(", ")
445 });
446
447 let unit_name_per_round = factory.map_or(String::from(no_tstates_msg), |tfactory| {
448 tfactory.unit_names().join(", ")
449 });
450
451 let code_distance_per_round = factory.map_or(String::from(no_tstates_msg), |tfactory| {
452 tfactory
453 .code_parameter_per_round()
454 .iter()
455 .map(|&num| num.copied().unwrap_or(1).to_string())
456 .collect::<Vec<_>>()
457 .join(", ")
458 });
459
460 let physical_qubits_per_round = factory.map_or(String::from(no_tstates_msg), |tfactory| {
461 tfactory
462 .physical_qubits_per_round()
463 .into_iter()
464 .map(format_metric_prefix)
465 .collect::<Vec<_>>()
466 .join(", ")
467 });
468
469 let tfactory_runtime_per_round = factory.map_or(String::from(no_tstates_msg), |tfactory| {
470 tfactory
471 .duration_per_round()
472 .iter()
473 .map(|&duration| format_duration(duration as u128))
474 .collect::<Vec<_>>()
475 .join(", ")
476 });
477
478 let tstate_logical_error_rate = factory.map_or(String::from(no_tstates_msg), |tfactory| {
479 format!("{:.2e}", tfactory.output_error_rate())
480 });
481
482 // Pre-layout logical resources
483 let logical_counts_num_qubits = format_metric_prefix(layout_report_data.num_qubits());
484 let logical_counts_t_count = format_metric_prefix(layout_report_data.t_count());
485 let logical_counts_rotation_count =
486 format_metric_prefix(layout_report_data.rotation_count());
487 let logical_counts_rotation_depth =
488 format_metric_prefix(layout_report_data.rotation_depth());
489 let logical_counts_ccz_count = format_metric_prefix(layout_report_data.ccz_count());
490 let logical_counts_ccix_count = format_metric_prefix(layout_report_data.ccix_count());
491 let logical_counts_measurement_count =
492 format_metric_prefix(layout_report_data.measurement_count());
493
494 // Assumed error budget
495 let error_budget = format!("{:.2e}", job_params.error_budget().total());
496 let error_budget_logical = format!("{:.2e}", result.error_budget().logical());
497 let error_budget_tstates = format!("{:.2e}", result.error_budget().magic_states());
498 let error_budget_rotations = format!("{:.2e}", result.error_budget().rotations());
499
500 let num_ts_per_rotation = layout_report_data
501 .num_ts_per_rotation(result.error_budget().rotations())
502 .map_or_else(|| String::from(no_rotations_msg), format_metric_prefix);
503
504 let constraint_not_set_msg = "constraint not set";
505
506 let logical_depth_factor = job_params
507 .constraints()
508 .logical_depth_factor
509 .map_or(String::from(constraint_not_set_msg), |v| format!("{v}"));
510 let max_t_factories = job_params
511 .constraints()
512 .max_t_factories
513 .map_or(String::from(constraint_not_set_msg), |v| format!("{v}"));
514 let max_duration = job_params
515 .constraints()
516 .max_duration
517 .map_or(String::from(constraint_not_set_msg), |v| format!("{v}"));
518 let max_physical_qubits = job_params
519 .constraints()
520 .max_physical_qubits
521 .map_or(String::from(constraint_not_set_msg), |v| format!("{v}"));
522
523 Self {
524 runtime,
525 rqops,
526 physical_qubits,
527 algorithmic_logical_qubits,
528 algorithmic_logical_depth,
529 logical_depth,
530 num_tstates,
531 num_tfactories,
532 num_tfactory_runs,
533 physical_qubits_for_algorithm,
534 physical_qubits_for_tfactories,
535 physical_qubits_for_tfactories_percentage,
536 required_logical_qubit_error_rate,
537 required_logical_tstate_error_rate,
538 physical_qubits_per_logical_qubit,
539 logical_cycle_time,
540 clock_frequency,
541 logical_error_rate,
542 tfactory_physical_qubits,
543 tfactory_runtime,
544 num_input_tstates,
545 num_units_per_round,
546 unit_name_per_round,
547 code_distance_per_round,
548 physical_qubits_per_round,
549 tfactory_runtime_per_round,
550 tstate_logical_error_rate,
551 logical_counts_num_qubits,
552 logical_counts_t_count,
553 logical_counts_rotation_count,
554 logical_counts_rotation_depth,
555 logical_counts_ccz_count,
556 logical_counts_ccix_count,
557 logical_counts_measurement_count,
558 error_budget,
559 error_budget_logical,
560 error_budget_tstates,
561 error_budget_rotations,
562 num_ts_per_rotation,
563 logical_depth_factor,
564 max_t_factories,
565 max_duration,
566 max_physical_qubits,
567 }
568 }
569}
570
571fn format_thousand_sep(val: &impl ToString) -> String {
572 val.to_string()
573 .as_bytes()
574 .rchunks(3)
575 .rev()
576 .map(std::str::from_utf8)
577 .collect::<Result<Vec<&str>, _>>()
578 .expect("Invalid utf-8 encoding")
579 .join(",")
580}
581
582fn format_thousand_sep_f64(val: f64) -> String {
583 // 92592.5925925926
584
585 let formatted = format!("{val:.2}");
586 // SAFETY: formatting guarantees that there is a .
587 let (i, f) = formatted.split_once('.').expect("Invalid formatting");
588
589 format!("{}.{f}", format_thousand_sep(&i))
590}
591
592#[must_use]
593pub fn format_metric_prefix(val: u64) -> String {
594 if val < 1000 {
595 val.to_string()
596 } else {
597 let prefixes = b"kMGTPEZYRQ";
598
599 let mut prefix_index = 0;
600
601 let mut val = val as f64 / 1e3;
602
603 loop {
604 let next_val = val / 1e3;
605 if next_val < 1.0 {
606 break;
607 }
608
609 prefix_index += 1;
610 val = next_val;
611 }
612
613 format!(
614 "{:.2}{}",
615 val,
616 std::str::from_utf8(prefixes.get(prefix_index..=prefix_index).unwrap_or(b"?"))
617 .expect("Invalid utf-8 encoding")
618 .trim()
619 )
620 }
621}
622
623#[must_use]
624pub fn format_duration(nanos: u128) -> String {
625 let units = [
626 ("nanosecs", 1),
627 ("microsecs", 1000),
628 ("millisecs", 1000),
629 ("secs", 1000),
630 ("mins", 60),
631 ("hours", 60),
632 ("days", 24),
633 ("years", 365),
634 ];
635
636 let (mut runtime, mut rem) = (nanos, 0u128);
637 let mut runtime_formatted = None;
638
639 for idx in 1..units.len() {
640 if runtime / units[idx].1 == 0 {
641 if rem >= units[idx - 1].1 / 2 {
642 runtime += 1;
643 }
644 runtime_formatted = Some(format!("{runtime} {}", units[idx - 1].0));
645 break;
646 }
647
648 (runtime, rem) = (runtime / units[idx].1, runtime % units[idx].1);
649 }
650
651 runtime_formatted.unwrap_or_else(|| format!("{runtime} {}", units[units.len() - 1].0))
652}
653