From 72c32aab7dce1098b5b794a906727741897caf9b Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Fri, 6 Sep 2024 09:58:04 -0700 Subject: [PATCH] Generic code with code distance and threshold (#1896) Provides a generic version for an error correction code that has a code distance, threshold, and exponential error suppression. The system version (with surface code and Floquet code as defaults, and customizable via formula strings) is now implemented based on the generic version. The generic version can also be used in other projects via extensibility API. --- resource_estimator/src/estimates.rs | 2 + .../src/estimates/error_correction.rs | 151 ++++ resource_estimator/src/system.rs | 3 +- .../src/system/modeling/fault_tolerance.rs | 703 ++++++++---------- .../system/modeling/fault_tolerance/tests.rs | 4 +- .../system/modeling/physical_qubit/tests.rs | 4 +- .../src/system/modeling/tfactory/tests.rs | 5 +- .../distillation_units_map/tests.rs | 26 +- .../optimization/tfactory_exhaustive/tests.rs | 23 +- resource_estimator/src/system/tests.rs | 49 +- 10 files changed, 523 insertions(+), 447 deletions(-) create mode 100644 resource_estimator/src/estimates/error_correction.rs diff --git a/resource_estimator/src/estimates.rs b/resource_estimator/src/estimates.rs index 9e631fd50c..1065f7a8e1 100644 --- a/resource_estimator/src/estimates.rs +++ b/resource_estimator/src/estimates.rs @@ -5,6 +5,8 @@ mod error; pub use error::Error; mod error_budget; pub use error_budget::ErrorBudget; +mod error_correction; +pub use error_correction::{CodeWithThresholdAndDistance, CodeWithThresholdAndDistanceEvaluator}; mod factory; pub use factory::{ BuilderDispatch2, DistillationRound, DistillationUnit, FactoryBuildError, FactoryDispatch2, diff --git a/resource_estimator/src/estimates/error_correction.rs b/resource_estimator/src/estimates/error_correction.rs new file mode 100644 index 0000000000..2a9900f1ef --- /dev/null +++ b/resource_estimator/src/estimates/error_correction.rs @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use super::ErrorCorrection; + +pub trait CodeWithThresholdAndDistanceEvaluator { + type Qubit; + + fn physical_error_rate(&self, qubit: &Self::Qubit) -> f64; + fn physical_qubits(&self, code_distance: u64) -> Result; + fn logical_cycle_time(&self, qubit: &Self::Qubit, code_distance: u64) -> Result; +} + +pub struct CodeWithThresholdAndDistance { + evaluator: Evaluator, + crossing_prefactor: f64, + error_correction_threshold: f64, + max_code_distance: Option, +} + +impl CodeWithThresholdAndDistance { + pub fn new( + evaluator: Evaluator, + crossing_prefactor: f64, + error_correction_threshold: f64, + ) -> Self { + Self { + evaluator, + crossing_prefactor, + error_correction_threshold, + max_code_distance: None, + } + } + + pub fn with_max_code_distance( + evaluator: Evaluator, + crossing_prefactor: f64, + error_correction_threshold: f64, + max_code_distance: u64, + ) -> Self { + Self { + evaluator, + crossing_prefactor, + error_correction_threshold, + max_code_distance: Some(max_code_distance), + } + } + + pub fn crossing_prefactor(&self) -> f64 { + self.crossing_prefactor + } + + pub fn set_crossing_prefactor(&mut self, crossing_prefactor: f64) { + self.crossing_prefactor = crossing_prefactor; + } + + pub fn error_correction_threshold(&self) -> f64 { + self.error_correction_threshold + } + + pub fn set_error_correction_threshold(&mut self, error_correction_threshold: f64) { + self.error_correction_threshold = error_correction_threshold; + } + + pub fn max_code_distance(&self) -> Option<&u64> { + self.max_code_distance.as_ref() + } + + pub fn set_max_code_distance(&mut self, max_code_distance: u64) { + self.max_code_distance = Some(max_code_distance); + } + + pub fn evaluator(&self) -> &Evaluator { + &self.evaluator + } + + pub fn evaluator_mut(&mut self) -> &mut Evaluator { + &mut self.evaluator + } +} + +impl ErrorCorrection + for CodeWithThresholdAndDistance +{ + type Qubit = Evaluator::Qubit; + type Parameter = u64; + + fn physical_qubits(&self, code_distance: &u64) -> Result { + self.evaluator.physical_qubits(*code_distance) + } + + fn logical_qubits(&self, _code_distance: &u64) -> Result { + Ok(1) + } + + fn logical_cycle_time(&self, qubit: &Self::Qubit, code_distance: &u64) -> Result { + self.evaluator.logical_cycle_time(qubit, *code_distance) + } + + fn logical_error_rate(&self, qubit: &Self::Qubit, code_distance: &u64) -> Result { + let physical_error_rate = self.evaluator.physical_error_rate(qubit); + + if physical_error_rate > self.error_correction_threshold { + Err(format!( + "invalid value for 'physical_error_rate', expected value between 0 and {}", + self.error_correction_threshold + )) + } else { + Ok(self.crossing_prefactor + * ((physical_error_rate / self.error_correction_threshold) + .powi((*code_distance as i32 + 1) / 2))) + } + } + + // Compute code distance d (Equation (E2) in paper) + fn compute_code_parameter( + &self, + qubit: &Self::Qubit, + required_logical_qubit_error_rate: f64, + ) -> Result { + let physical_error_rate = self.evaluator.physical_error_rate(qubit); + let numerator = 2.0 * (self.crossing_prefactor / required_logical_qubit_error_rate).ln(); + let denominator = (self.error_correction_threshold / physical_error_rate).ln(); + + let code_distance = (((numerator / denominator) - 1.0).ceil() as u64) | 0x1; + + if let Some(max_distance) = self.max_code_distance { + if max_distance < code_distance { + return Err(format!("The computed code distance {code_distance} is too high; maximum allowed code distance is {max_distance}; try increasing the total logical error budget")); + } + } + + Ok(code_distance) + } + + fn code_parameter_range( + &self, + lower_bound: Option<&Self::Parameter>, + ) -> impl Iterator { + (lower_bound.copied().unwrap_or(1)..=self.max_code_distance.unwrap_or(u64::MAX)).step_by(2) + } + + fn code_parameter_cmp( + &self, + _qubit: &Self::Qubit, + p1: &Self::Parameter, + p2: &Self::Parameter, + ) -> std::cmp::Ordering { + p1.cmp(p2) + } +} diff --git a/resource_estimator/src/system.rs b/resource_estimator/src/system.rs index 711756f296..5e35e132a4 100644 --- a/resource_estimator/src/system.rs +++ b/resource_estimator/src/system.rs @@ -29,6 +29,7 @@ pub use self::optimization::TFactoryBuilder; pub use self::{data::LogicalResourceCounts, error::Error}; use data::{EstimateType, JobParams}; pub use data::{LayoutReportData, PartitioningOverhead}; +use modeling::load_protocol_from_specification; use serde::Serialize; pub(crate) type Result = std::result::Result; @@ -77,7 +78,7 @@ fn estimate_single Result> { let qubit = job_params.qubit_params().clone(); - let ftp = Protocol::load_from_specification(job_params.qec_scheme_mut(), &qubit)?; + let ftp = load_protocol_from_specification(job_params.qec_scheme_mut(), &qubit)?; let distillation_unit_templates = job_params .distillation_unit_specifications() .as_templates()?; diff --git a/resource_estimator/src/system/modeling/fault_tolerance.rs b/resource_estimator/src/system/modeling/fault_tolerance.rs index 59ad3f25fc..3bb11aa0a1 100644 --- a/resource_estimator/src/system/modeling/fault_tolerance.rs +++ b/resource_estimator/src/system/modeling/fault_tolerance.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use crate::estimates::{ + CodeWithThresholdAndDistance, CodeWithThresholdAndDistanceEvaluator, Error::{LogicalCycleTimeComputationFailed, PhysicalQubitComputationFailed}, ErrorCorrection, }; @@ -46,7 +47,7 @@ pub struct ProtocolSpecification { pub(crate) logical_cycle_time: Option, #[serde(default)] pub(crate) physical_qubits_per_logical_qubit: Option, - #[serde(default = "Protocol::default_max_code_distance")] + #[serde(default = "default_max_code_distance")] pub(crate) max_code_distance: u64, } @@ -58,7 +59,7 @@ impl Default for ProtocolSpecification { error_correction_threshold: None, logical_cycle_time: None, physical_qubits_per_logical_qubit: None, - max_code_distance: Protocol::default_max_code_distance(), + max_code_distance: default_max_code_distance(), } } } @@ -79,326 +80,16 @@ impl Default for ProtocolSpecification { /// /// Note that all physical qubit related variables are not available as variable /// in formulas for `physical_qubits_per_logical_qubit`. -#[derive(Debug)] -pub struct Protocol { - error_correction_threshold: f64, - crossing_prefactor: f64, +pub type Protocol = CodeWithThresholdAndDistance; + +pub struct ProtocolEvaluator { logical_cycle_time_expr: String, logical_cycle_time: CompiledExpression, physical_qubits_per_logical_qubit_expr: String, physical_qubits_per_logical_qubit: CompiledExpression, - max_code_distance: u64, } -impl Default for Protocol { - fn default() -> Self { - Self::surface_code_gate_based() - } -} - -impl Protocol { - /// Loads and validates a fault-tolerance protocol from a fault-tolerance model - pub(crate) fn load_from_specification( - model: &mut ProtocolSpecification, - qubit: &PhysicalQubit, - ) -> crate::system::Result { - let (mut ftp, predefined) = Self::base_protocol(model, qubit)?; - - if predefined { - ftp.update_default_from_specification(model)?; - } - - if ftp.crossing_prefactor > 0.5 { - return Err(Error::InvalidValue( - String::from("crossingPrefactor"), - 0.0, - 0.5, - )); - } - - // validate model with respect to qubit - if qubit.clifford_error_rate() >= ftp.error_correction_threshold { - match qubit.instruction_set() { - PhysicalInstructionSet::GateBased => { - return Err(Error::InvalidValue( - format!( - "{ONE_QUBIT_GATE_ERROR_RATE}, {TWO_QUBIT_GATE_ERROR_RATE}, {IDLE_ERROR_RATE}" - ), - 0.0, - ftp.error_correction_threshold, - )) - } - PhysicalInstructionSet::Majorana => { - return Err(Error::InvalidValue( - format!( - "{IDLE_ERROR_RATE}, {ONE_QUBIT_MEASUREMENT_PROCESS_ERROR_RATE}, {TWO_QUBIT_JOINT_MEASUREMENT_PROCESS_ERROR_RATE}", - ), - 0.0, - ftp.error_correction_threshold, - )) - } - } - } - - // validate that formulas only yield positive values - for code_distance in (1..=model.max_code_distance).skip(2) { - // can you compute logical cycle time and number of physical qubits with code distance? - ftp.logical_cycle_time(qubit, &code_distance) - .map_err(LogicalCycleTimeComputationFailed)?; - ftp.physical_qubits(&code_distance) - .map_err(PhysicalQubitComputationFailed)?; - } - - Ok(ftp) - } - - fn base_protocol( - model: &mut ProtocolSpecification, - qubit: &PhysicalQubit, - ) -> crate::system::Result<(Self, bool)> { - if model.name == "surface_code" - || model.name == "surfaceCode" - || model.name == "surface-code" - { - match qubit.instruction_set() { - PhysicalInstructionSet::GateBased => Ok((Self::surface_code_gate_based(), true)), - PhysicalInstructionSet::Majorana => { - Ok((Self::surface_code_measurement_based(), true)) - } - } - } else if model.name == "floquet_code" - || model.name == "floquetCode" - || model.name == "floquet-code" - { - match qubit.instruction_set() { - PhysicalInstructionSet::GateBased => Err(InvalidFaultToleranceProtocol.into()), - PhysicalInstructionSet::Majorana => Ok((Self::floquet_code(), true)), - } - } else { - let error_correction_threshold = model.error_correction_threshold.ok_or_else(|| { - CannotParseJSON(serde::de::Error::missing_field("errorCorrectionThreshold")) - })?; - let crossing_prefactor = model.crossing_prefactor.ok_or_else(|| { - CannotParseJSON(serde::de::Error::missing_field("crossingPrefactor")) - })?; - - let logical_cycle_time_expr = model - .logical_cycle_time - .as_ref() - .ok_or_else(|| { - CannotParseJSON(serde::de::Error::missing_field("logicalCycleTime")) - })? - .clone(); - - let physical_qubits_per_logical_qubit_expr = model - .physical_qubits_per_logical_qubit - .as_ref() - .ok_or_else(|| { - CannotParseJSON(serde::de::Error::missing_field( - "physicalQubitsPerLogicalQubit", - )) - })? - .clone(); - - let (logical_cycle_time, physical_qubits_per_logical_qubit) = - Protocol::parse_compiled_expressions( - &logical_cycle_time_expr, - &physical_qubits_per_logical_qubit_expr, - )?; - - let max_code_distance = model.max_code_distance; - - Ok(( - Self { - error_correction_threshold, - crossing_prefactor, - logical_cycle_time_expr, - logical_cycle_time, - physical_qubits_per_logical_qubit_expr, - physical_qubits_per_logical_qubit, - max_code_distance, - }, - false, - )) - } - } - - /// Assumes that self is initialized to a default model based on a - /// predefined specification; then either updates `self` based on - /// additionally set values in `model`, or initializes non-assigned values - /// in `model` from the predefined ones. - fn update_default_from_specification( - &mut self, - model: &mut ProtocolSpecification, - ) -> crate::system::Result<()> { - if let Some(error_correction_threshold) = model.error_correction_threshold { - self.error_correction_threshold = error_correction_threshold; - } else { - model.error_correction_threshold = Some(self.error_correction_threshold); - } - - if let Some(crossing_prefactor) = model.crossing_prefactor { - self.crossing_prefactor = crossing_prefactor; - } else { - model.crossing_prefactor = Some(self.crossing_prefactor); - } - - if let Some(logical_cycle_time) = model.logical_cycle_time.as_ref() { - self.logical_cycle_time = - CompiledExpression::from_string(logical_cycle_time, "logicalCycleTime")?; - } else { - model.logical_cycle_time = Some(self.logical_cycle_time_expr.clone()); - } - - if let Some(physical_qubits_per_logical_qubit) = - model.physical_qubits_per_logical_qubit.as_ref() - { - self.physical_qubits_per_logical_qubit = CompiledExpression::from_string( - physical_qubits_per_logical_qubit, - "physicalQubitsPerLogicalQubit", - )?; - } else { - model.physical_qubits_per_logical_qubit = - Some(self.physical_qubits_per_logical_qubit_expr.clone()); - } - - self.max_code_distance = model.max_code_distance; - - Ok(()) - } - - /// Default floquet code FTP for gate based qubits - /// - /// ```yaml - /// name: "surface_code" - /// instruction_set: "gate_based" - /// # [arXiv:1208.0928, Eq. (13)] - /// # [arXiv:1009.3686, Figs. 6-7] - /// error_correction_threshold: 0.01 - /// # [arXiv:1208.0928, Eq. (11)] - /// crossing_prefactor: 0.03 - /// logical_cycle_time: "(4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance" - /// physical_qubits_per_logical_qubit: "2 * codeDistance * codeDistance" - /// ``` - #[must_use] - pub fn surface_code_gate_based() -> Self { - // [arXiv:1208.0928, Eq. (13)] - // [arXiv:1009.3686, Figs. 6-7] - let error_correction_threshold = 0.01; - // [arXiv:1208.0928, Eq. (11)] - let crossing_prefactor = 0.03; - - let logical_cycle_time_expr = format!( - "(4 * {TWO_QUBIT_GATE_TIME} + 2 * {ONE_QUBIT_MEASUREMENT_TIME}) * codeDistance" - ); - let physical_qubits_per_logical_qubit_expr = - String::from("2 * codeDistance * codeDistance"); - - let (logical_cycle_time, physical_qubits_per_logical_qubit) = - Protocol::parse_compiled_expressions( - &logical_cycle_time_expr, - &physical_qubits_per_logical_qubit_expr, - ) - .expect("could not parse expressions"); - - Self { - error_correction_threshold, - crossing_prefactor, - logical_cycle_time_expr, - logical_cycle_time, - physical_qubits_per_logical_qubit_expr, - physical_qubits_per_logical_qubit, - max_code_distance: Self::default_max_code_distance(), - } - } - - /// Default floquet code FTP for measurement based qubits - /// - /// ```yaml - /// name: "surface_code" - /// instruction_set: "measurement_based" - /// # [arXiv:2007.00307, Eq. (1)] - /// error_correction_threshold: 0.0015 - /// crossing_prefactor: 0.08 - /// logical_cycle_time: "20 * oneQubitMeasurementTime * codeDistance" - /// physical_qubits_per_logical_qubit: "2 * codeDistance * codeDistance" - /// ``` - #[must_use] - pub fn surface_code_measurement_based() -> Self { - // [arXiv:2007.00307, Eq. (1)] - let error_correction_threshold = 0.0015; - let crossing_prefactor = 0.08; - - let logical_cycle_time_expr = format!("20 * {ONE_QUBIT_MEASUREMENT_TIME} * codeDistance"); - let physical_qubits_per_logical_qubit_expr = - String::from("2 * codeDistance * codeDistance"); - - let (logical_cycle_time, physical_qubits_per_logical_qubit) = - Protocol::parse_compiled_expressions( - &logical_cycle_time_expr, - &physical_qubits_per_logical_qubit_expr, - ) - .expect("could not parse expressions"); - - Self { - error_correction_threshold, - crossing_prefactor, - logical_cycle_time_expr, - logical_cycle_time, - physical_qubits_per_logical_qubit_expr, - physical_qubits_per_logical_qubit, - max_code_distance: Self::default_max_code_distance(), - } - } - - /// Default floquet code FTP for measurement based qubits - /// - /// ```yaml - /// name: "floquet_code" - /// instruction_set: "measurement_based" - /// error_correction_threshold: 0.01 - /// crossing_prefactor: 0.07 - /// logical_cycle_time: "3 * oneQubitMeasurementTime * codeDistance" - /// # [arXiv:2202.11829, Table 2] - /// physical_qubits_per_logical_qubit: "4 * codeDistance * codeDistance + 8 * (codeDistance - 1)" - /// ``` - #[must_use] - pub fn floquet_code() -> Self { - let error_correction_threshold = 0.01; - let crossing_prefactor = 0.07; - let logical_cycle_time_expr = format!("3 * {ONE_QUBIT_MEASUREMENT_TIME} * codeDistance"); - // [arXiv:2202.11829, Table 2] - let physical_qubits_per_logical_qubit_expr = - String::from("4 * codeDistance * codeDistance + 8 * (codeDistance - 1)"); - - let (logical_cycle_time, physical_qubits_per_logical_qubit) = - Protocol::parse_compiled_expressions( - &logical_cycle_time_expr, - &physical_qubits_per_logical_qubit_expr, - ) - .expect("could not parse expressions"); - - Self { - error_correction_threshold, - crossing_prefactor, - logical_cycle_time_expr, - logical_cycle_time, - physical_qubits_per_logical_qubit_expr, - physical_qubits_per_logical_qubit, - max_code_distance: Self::default_max_code_distance(), - } - } - - /// Returns the fault-tolerance protocol's crossing prefactor. - fn crossing_prefactor(&self) -> f64 { - self.crossing_prefactor - } - - /// Returns the fault-tolerance protocol's error-correction threshold. - fn error_correction_threshold(&self) -> f64 { - self.error_correction_threshold - } - +impl ProtocolEvaluator { /// Creates an evaluation context for formulas specified in fault-tolerance /// protocol files. /// @@ -439,7 +130,7 @@ impl Protocol { context } - fn parse_compiled_expressions( + pub fn parse_compiled_expressions( logical_cycle_time_expr: &str, physical_qubits_per_logical_qubit_expr: &str, ) -> crate::system::Result<(CompiledExpression, CompiledExpression)> { @@ -451,59 +142,31 @@ impl Protocol { )?, )) } - - #[cfg(test)] - #[must_use] - pub fn max_code_distance(&self) -> u64 { - self.max_code_distance - } - - #[inline] - fn default_max_code_distance() -> u64 { - MAX_CODE_DISTANCE - } } -impl ErrorCorrection for Protocol { +impl CodeWithThresholdAndDistanceEvaluator for ProtocolEvaluator { type Qubit = PhysicalQubit; - type Parameter = u64; - /// Computes the number of physical qubits required for this code - /// - /// The formula for this field has a default value of `2 * code_distance * - /// code_distance`. - fn physical_qubits(&self, code_distance: &u64) -> Result { - let mut context = Self::create_evaluation_context(None, *code_distance); + fn physical_error_rate(&self, qubit: &Self::Qubit) -> f64 { + qubit.clifford_error_rate().max(qubit.readout_error_rate()) + } + + fn physical_qubits(&self, code_distance: u64) -> Result { + let mut context = Self::create_evaluation_context(None, code_distance); let value = self .physical_qubits_per_logical_qubit .evaluate(&mut context) .map_err(|err| err.to_string())?; if value <= 0.0 { - Err(NonPositivePhysicalQubitsPerLogicalQubit(*code_distance).to_string()) + Err(NonPositivePhysicalQubitsPerLogicalQubit(code_distance).to_string()) } else { Ok(value as u64) } } - /// The planar codes considered in this system encode 1 logical qubit - fn logical_qubits(&self, _code_parameter: &Self::Parameter) -> Result { - Ok(1) - } - - /// Returns the time of one logical cycle. - /// - /// The logical cycle time is the time it takes to perform `code_distance` - /// rounds of quantum error correction. The latter, also called syndrome - /// extraction time, is based on physical operation times specified in the - /// qubit and usually some factor based on the choice of stabilizer - /// extraction circuit. - fn logical_cycle_time( - &self, - qubit: &PhysicalQubit, - code_distance: &u64, - ) -> Result { - let mut context = Self::create_evaluation_context(Some(qubit), *code_distance); + fn logical_cycle_time(&self, qubit: &Self::Qubit, code_distance: u64) -> Result { + let mut context = Self::create_evaluation_context(Some(qubit), code_distance); let result = self .logical_cycle_time @@ -511,61 +174,307 @@ impl ErrorCorrection for Protocol { .map_err(|err| err.to_string())?; if result <= 0.0 { - Err(NonPositiveLogicalCycleTime(*code_distance).to_string()) + Err(NonPositiveLogicalCycleTime(code_distance).to_string()) } else { Ok(result.round() as u64) } } +} - /// Computes the logical failure probability. - /// - /// Computes the logical failure probability based on a physical error rate - /// and a code distance - fn logical_error_rate( - &self, - qubit: &PhysicalQubit, - code_distance: &u64, - ) -> Result { - let physical_error_rate = qubit.clifford_error_rate().max(qubit.readout_error_rate()); - - if physical_error_rate > self.error_correction_threshold() { - Err(Error::InvalidValue( - String::from("physical_error_rate"), - 0.0, - self.error_correction_threshold, - ) - .to_string()) - } else { - Ok(self.crossing_prefactor() - * ((physical_error_rate / self.error_correction_threshold()) - .powi((*code_distance as i32 + 1) / 2))) - } +pub fn load_protocol_from_specification( + model: &mut ProtocolSpecification, + qubit: &PhysicalQubit, +) -> crate::system::Result> { + let (mut ftp, predefined) = base_protocol(model, qubit)?; + + if predefined { + update_default_from_specification(&mut ftp, model)?; } - // Compute code distance d (Equation (E2) in paper) - fn compute_code_parameter( - &self, - qubit: &PhysicalQubit, - required_logical_qubit_error_rate: f64, - ) -> Result { - let physical_error_rate = qubit.clifford_error_rate().max(qubit.readout_error_rate()); - let numerator = 2.0 * (self.crossing_prefactor() / required_logical_qubit_error_rate).ln(); - let denominator = (self.error_correction_threshold() / physical_error_rate).ln(); + if ftp.crossing_prefactor() > 0.5 { + return Err(Error::InvalidValue( + String::from("crossingPrefactor"), + 0.0, + 0.5, + )); + } - let code_distance = (((numerator / denominator) - 1.0).ceil() as u64) | 0x1; + // validate model with respect to qubit + if qubit.clifford_error_rate() >= ftp.error_correction_threshold() { + match qubit.instruction_set() { + PhysicalInstructionSet::GateBased => { + return Err(Error::InvalidValue( + format!( + "{ONE_QUBIT_GATE_ERROR_RATE}, {TWO_QUBIT_GATE_ERROR_RATE}, {IDLE_ERROR_RATE}" + ), + 0.0, + ftp.error_correction_threshold(), + )) + } + PhysicalInstructionSet::Majorana => { + return Err(Error::InvalidValue( + format!( + "{IDLE_ERROR_RATE}, {ONE_QUBIT_MEASUREMENT_PROCESS_ERROR_RATE}, {TWO_QUBIT_JOINT_MEASUREMENT_PROCESS_ERROR_RATE}", + ), + 0.0, + ftp.error_correction_threshold(), + )) + } + } + } - if code_distance <= self.max_code_distance { - Ok(code_distance) - } else { - Err(format!("The computed code distance {code_distance} is too high; maximum allowed code distance is {}; try increasing the total logical error budget", self.max_code_distance)) + // validate that formulas only yield positive values + for code_distance in (1..=model.max_code_distance).skip(2) { + // can you compute logical cycle time and number of physical qubits with code distance? + ftp.logical_cycle_time(qubit, &code_distance) + .map_err(LogicalCycleTimeComputationFailed)?; + ftp.physical_qubits(&code_distance) + .map_err(PhysicalQubitComputationFailed)?; + } + + Ok(ftp) +} + +fn base_protocol( + model: &mut ProtocolSpecification, + qubit: &PhysicalQubit, +) -> crate::system::Result<(CodeWithThresholdAndDistance, bool)> { + if model.name == "surface_code" || model.name == "surfaceCode" || model.name == "surface-code" { + match qubit.instruction_set() { + PhysicalInstructionSet::GateBased => Ok((surface_code_gate_based(), true)), + PhysicalInstructionSet::Majorana => Ok((surface_code_measurement_based(), true)), + } + } else if model.name == "floquet_code" + || model.name == "floquetCode" + || model.name == "floquet-code" + { + match qubit.instruction_set() { + PhysicalInstructionSet::GateBased => Err(InvalidFaultToleranceProtocol.into()), + PhysicalInstructionSet::Majorana => Ok((floquet_code(), true)), } + } else { + let error_correction_threshold = model.error_correction_threshold.ok_or_else(|| { + CannotParseJSON(serde::de::Error::missing_field("errorCorrectionThreshold")) + })?; + let crossing_prefactor = model + .crossing_prefactor + .ok_or_else(|| CannotParseJSON(serde::de::Error::missing_field("crossingPrefactor")))?; + + let logical_cycle_time_expr = model + .logical_cycle_time + .as_ref() + .ok_or_else(|| CannotParseJSON(serde::de::Error::missing_field("logicalCycleTime")))? + .clone(); + + let physical_qubits_per_logical_qubit_expr = model + .physical_qubits_per_logical_qubit + .as_ref() + .ok_or_else(|| { + CannotParseJSON(serde::de::Error::missing_field( + "physicalQubitsPerLogicalQubit", + )) + })? + .clone(); + + let (logical_cycle_time, physical_qubits_per_logical_qubit) = + ProtocolEvaluator::parse_compiled_expressions( + &logical_cycle_time_expr, + &physical_qubits_per_logical_qubit_expr, + )?; + + let max_code_distance = model.max_code_distance; + + let evaluator = ProtocolEvaluator { + logical_cycle_time_expr, + logical_cycle_time, + physical_qubits_per_logical_qubit_expr, + physical_qubits_per_logical_qubit, + }; + + Ok(( + CodeWithThresholdAndDistance::with_max_code_distance( + evaluator, + crossing_prefactor, + error_correction_threshold, + max_code_distance, + ), + false, + )) + } +} + +/// Assumes that self is initialized to a default model based on a +/// predefined specification; then either updates `self` based on +/// additionally set values in `model`, or initializes non-assigned values +/// in `model` from the predefined ones. +fn update_default_from_specification( + code: &mut CodeWithThresholdAndDistance, + model: &mut ProtocolSpecification, +) -> crate::system::Result<()> { + if let Some(error_correction_threshold) = model.error_correction_threshold { + code.set_error_correction_threshold(error_correction_threshold); + } else { + model.error_correction_threshold = Some(code.error_correction_threshold()); + } + + if let Some(crossing_prefactor) = model.crossing_prefactor { + code.set_crossing_prefactor(crossing_prefactor); + } else { + model.crossing_prefactor = Some(code.crossing_prefactor()); } - fn code_parameter_range(&self, lower_bound: Option<&u64>) -> impl Iterator { - (lower_bound.copied().unwrap_or(1)..=self.max_code_distance).step_by(2) + if let Some(logical_cycle_time) = model.logical_cycle_time.as_ref() { + code.evaluator_mut().logical_cycle_time = + CompiledExpression::from_string(logical_cycle_time, "logicalCycleTime")?; + } else { + model.logical_cycle_time = Some(code.evaluator().logical_cycle_time_expr.clone()); } - fn code_parameter_cmp(&self, _qubit: &PhysicalQubit, p1: &u64, p2: &u64) -> std::cmp::Ordering { - p1.cmp(p2) + if let Some(physical_qubits_per_logical_qubit) = + model.physical_qubits_per_logical_qubit.as_ref() + { + code.evaluator_mut().physical_qubits_per_logical_qubit = CompiledExpression::from_string( + physical_qubits_per_logical_qubit, + "physicalQubitsPerLogicalQubit", + )?; + } else { + model.physical_qubits_per_logical_qubit = Some( + code.evaluator() + .physical_qubits_per_logical_qubit_expr + .clone(), + ); } + + code.set_max_code_distance(model.max_code_distance); + + Ok(()) +} + +fn default_max_code_distance() -> u64 { + MAX_CODE_DISTANCE +} + +/// Default floquet code FTP for gate based qubits +/// +/// ```yaml +/// name: "surface_code" +/// instruction_set: "gate_based" +/// # [arXiv:1208.0928, Eq. (13)] +/// # [arXiv:1009.3686, Figs. 6-7] +/// error_correction_threshold: 0.01 +/// # [arXiv:1208.0928, Eq. (11)] +/// crossing_prefactor: 0.03 +/// logical_cycle_time: "(4 * twoQubitGateTime + 2 * oneQubitMeasurementTime) * codeDistance" +/// physical_qubits_per_logical_qubit: "2 * codeDistance * codeDistance" +/// ``` +#[must_use] +pub fn surface_code_gate_based() -> CodeWithThresholdAndDistance { + // [arXiv:1208.0928, Eq. (13)] + // [arXiv:1009.3686, Figs. 6-7] + let error_correction_threshold = 0.01; + // [arXiv:1208.0928, Eq. (11)] + let crossing_prefactor = 0.03; + + let logical_cycle_time_expr = + format!("(4 * {TWO_QUBIT_GATE_TIME} + 2 * {ONE_QUBIT_MEASUREMENT_TIME}) * codeDistance"); + let physical_qubits_per_logical_qubit_expr = String::from("2 * codeDistance * codeDistance"); + + let (logical_cycle_time, physical_qubits_per_logical_qubit) = + ProtocolEvaluator::parse_compiled_expressions( + &logical_cycle_time_expr, + &physical_qubits_per_logical_qubit_expr, + ) + .expect("could not parse expressions"); + + CodeWithThresholdAndDistance::with_max_code_distance( + ProtocolEvaluator { + logical_cycle_time_expr, + logical_cycle_time, + physical_qubits_per_logical_qubit_expr, + physical_qubits_per_logical_qubit, + }, + crossing_prefactor, + error_correction_threshold, + MAX_CODE_DISTANCE, + ) +} + +/// Default floquet code FTP for measurement based qubits +/// +/// ```yaml +/// name: "surface_code" +/// instruction_set: "measurement_based" +/// # [arXiv:2007.00307, Eq. (1)] +/// error_correction_threshold: 0.0015 +/// crossing_prefactor: 0.08 +/// logical_cycle_time: "20 * oneQubitMeasurementTime * codeDistance" +/// physical_qubits_per_logical_qubit: "2 * codeDistance * codeDistance" +/// ``` +#[must_use] +pub fn surface_code_measurement_based() -> CodeWithThresholdAndDistance { + // [arXiv:2007.00307, Eq. (1)] + let error_correction_threshold = 0.0015; + let crossing_prefactor = 0.08; + + let logical_cycle_time_expr = format!("20 * {ONE_QUBIT_MEASUREMENT_TIME} * codeDistance"); + let physical_qubits_per_logical_qubit_expr = String::from("2 * codeDistance * codeDistance"); + + let (logical_cycle_time, physical_qubits_per_logical_qubit) = + ProtocolEvaluator::parse_compiled_expressions( + &logical_cycle_time_expr, + &physical_qubits_per_logical_qubit_expr, + ) + .expect("could not parse expressions"); + + CodeWithThresholdAndDistance::with_max_code_distance( + ProtocolEvaluator { + logical_cycle_time_expr, + logical_cycle_time, + physical_qubits_per_logical_qubit_expr, + physical_qubits_per_logical_qubit, + }, + crossing_prefactor, + error_correction_threshold, + MAX_CODE_DISTANCE, + ) +} + +/// Default floquet code FTP for measurement based qubits +/// +/// ```yaml +/// name: "floquet_code" +/// instruction_set: "measurement_based" +/// error_correction_threshold: 0.01 +/// crossing_prefactor: 0.07 +/// logical_cycle_time: "3 * oneQubitMeasurementTime * codeDistance" +/// # [arXiv:2202.11829, Table 2] +/// physical_qubits_per_logical_qubit: "4 * codeDistance * codeDistance + 8 * (codeDistance - 1)" +/// ``` +#[must_use] +pub fn floquet_code() -> CodeWithThresholdAndDistance { + let error_correction_threshold = 0.01; + let crossing_prefactor = 0.07; + let logical_cycle_time_expr = format!("3 * {ONE_QUBIT_MEASUREMENT_TIME} * codeDistance"); + // [arXiv:2202.11829, Table 2] + let physical_qubits_per_logical_qubit_expr = + String::from("4 * codeDistance * codeDistance + 8 * (codeDistance - 1)"); + + let (logical_cycle_time, physical_qubits_per_logical_qubit) = + ProtocolEvaluator::parse_compiled_expressions( + &logical_cycle_time_expr, + &physical_qubits_per_logical_qubit_expr, + ) + .expect("could not parse expressions"); + + CodeWithThresholdAndDistance::with_max_code_distance( + ProtocolEvaluator { + logical_cycle_time_expr, + logical_cycle_time, + physical_qubits_per_logical_qubit_expr, + physical_qubits_per_logical_qubit, + }, + crossing_prefactor, + error_correction_threshold, + MAX_CODE_DISTANCE, + ) } diff --git a/resource_estimator/src/system/modeling/fault_tolerance/tests.rs b/resource_estimator/src/system/modeling/fault_tolerance/tests.rs index ce486892a3..3318221088 100644 --- a/resource_estimator/src/system/modeling/fault_tolerance/tests.rs +++ b/resource_estimator/src/system/modeling/fault_tolerance/tests.rs @@ -3,13 +3,13 @@ use crate::{ estimates::ErrorCorrection, - system::modeling::{PhysicalQubit, Protocol}, + system::modeling::{surface_code_gate_based, PhysicalQubit}, }; #[test] fn compute_code_distance() -> Result<(), String> { let qubit = PhysicalQubit::default(); - let ftp = Protocol::surface_code_gate_based(); + let ftp = surface_code_gate_based(); assert!( (ftp.logical_error_rate(&qubit, &5)? - 3.000_000_000_000_000_8e-5).abs() <= f64::EPSILON ); diff --git a/resource_estimator/src/system/modeling/physical_qubit/tests.rs b/resource_estimator/src/system/modeling/physical_qubit/tests.rs index 04399916c9..3a6d8fb78b 100644 --- a/resource_estimator/src/system/modeling/physical_qubit/tests.rs +++ b/resource_estimator/src/system/modeling/physical_qubit/tests.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use crate::{ estimates::LogicalPatch, - system::{constants::FLOAT_COMPARISON_EPSILON, modeling::Protocol, Result}, + system::{constants::FLOAT_COMPARISON_EPSILON, modeling::surface_code_gate_based, Result}, }; use super::super::super::constants::{ @@ -283,7 +283,7 @@ fn load_physical_qubit_from_file() { fn logical_qubit_from_fast_gate_based_and_surface_code() -> Result<()> { let physical_qubit = Rc::new(PhysicalQubit::default()); - let ftp = Protocol::default(); + let ftp = surface_code_gate_based(); let logical_qubit = LogicalPatch::new(&ftp, 7, physical_qubit)?; diff --git a/resource_estimator/src/system/modeling/tfactory/tests.rs b/resource_estimator/src/system/modeling/tfactory/tests.rs index cda844897c..1351f739c1 100644 --- a/resource_estimator/src/system/modeling/tfactory/tests.rs +++ b/resource_estimator/src/system/modeling/tfactory/tests.rs @@ -8,6 +8,7 @@ use super::*; use std::rc::Rc; use crate::estimates::LogicalPatch; +use crate::system::modeling::surface_code_gate_based; use super::super::super::modeling::{PhysicalQubit, Protocol}; use super::super::super::{constants::FLOAT_COMPARISON_EPSILON, Result}; @@ -87,7 +88,7 @@ fn single_physical_qubit() { fn create_logical_qubit_with_distance( code_distance: u64, ) -> core::result::Result, crate::estimates::Error> { - let ftp = Protocol::default(); + let ftp = surface_code_gate_based(); let qubit = Rc::new(PhysicalQubit::default()); LogicalPatch::new(&ftp, code_distance, qubit) @@ -266,7 +267,7 @@ fn expression_by_formula2() { #[test] fn test_default_t_factory() { let physical_qubit = PhysicalQubit::default(); - let ftp = Protocol::default(); + let ftp = surface_code_gate_based(); let logical_qubit = LogicalPatch::new(&ftp, 15, Rc::new(physical_qubit)) .expect("logical qubit contruction should succeed"); let tfactory = default_t_factory(&logical_qubit); diff --git a/resource_estimator/src/system/optimization/distillation_units_map/tests.rs b/resource_estimator/src/system/optimization/distillation_units_map/tests.rs index 0159c9c105..710da5d704 100644 --- a/resource_estimator/src/system/optimization/distillation_units_map/tests.rs +++ b/resource_estimator/src/system/optimization/distillation_units_map/tests.rs @@ -12,8 +12,8 @@ use super::super::super::{ TFactoryProtocolSpecificDistillationUnitSpecification, }, modeling::{ - PhysicalQubit, Protocol, TFactoryDistillationUnit, TFactoryDistillationUnitTemplate, - TFactoryDistillationUnitType, + floquet_code, surface_code_gate_based, PhysicalQubit, Protocol, TFactoryDistillationUnit, + TFactoryDistillationUnitTemplate, TFactoryDistillationUnitType, }, }; @@ -181,7 +181,7 @@ fn create_default_t_factory_protocol_specific_distillation_unit_specification( fn units_for_distance_test_position_0_distance_1() { create_and_test( PhysicalQubit::qubit_maj_ns_e4(), - &Protocol::floquet_code(), + &floquet_code(), 0, 1, "physical", @@ -193,7 +193,7 @@ fn units_for_distance_test_position_0_distance_1() { fn units_for_distance_test_position_0_distance_3() { create_and_test( PhysicalQubit::default(), - &Protocol::default(), + &surface_code_gate_based(), 0, 3, "logical", @@ -205,7 +205,7 @@ fn units_for_distance_test_position_0_distance_3() { fn units_for_distance_test_position_1_distance_1() { create_and_test( PhysicalQubit::default(), - &Protocol::default(), + &surface_code_gate_based(), 1, 1, "logical", @@ -217,7 +217,7 @@ fn units_for_distance_test_position_1_distance_1() { fn units_for_distance_test_position_1_distance_5() { create_and_test( PhysicalQubit::default(), - &Protocol::default(), + &surface_code_gate_based(), 1, 5, "logical", @@ -270,7 +270,7 @@ fn create_templates_list_0_2_1() -> Vec { #[test] fn test_map_creation_no_purely_physical_templates_filtered_out_by_is_valid_condition() { let templates = create_templates_list_2_2_2(); - let ftp = Protocol::default(); + let ftp = surface_code_gate_based(); let map = create_default_map(PhysicalQubit::default(), &ftp, &templates); assert!(map.num_physical_distillation_units == 0); assert!(map.num_logical_distillation_units == 2); @@ -286,8 +286,8 @@ fn test_map_creation_no_purely_physical_templates_filtered_out_by_is_valid_condi #[test] fn test_map_creation_with_purely_physical_templates() { - let templates = create_templates_list_2_2_2(); - let ftp = Protocol::floquet_code(); + let templates: Vec = create_templates_list_2_2_2(); + let ftp = floquet_code(); let map = create_default_map(PhysicalQubit::qubit_maj_ns_e4(), &ftp, &templates); assert!(map.num_physical_distillation_units == 2); assert!(map.num_logical_distillation_units == 2); @@ -306,7 +306,7 @@ fn test_map_creation_with_purely_physical_templates() { #[test] fn test_map_creation_with_purely_physical_and_no_combined_templates() { let templates = create_templates_list_0_2_1(); - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let map = create_default_map(PhysicalQubit::qubit_maj_ns_e4(), &ftp, &templates); assert!(map.num_physical_distillation_units == 1); assert!(map.num_logical_distillation_units == 2); @@ -321,7 +321,7 @@ fn test_first_round_overrides_applied() { let mut templates = create_templates_list_2_2_2(); templates.push(create_distillation_unit_template_with_override()); templates.push(create_distillation_unit_template_without_override()); - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let map = create_default_map(PhysicalQubit::qubit_maj_ns_e4(), &ftp, &templates); assert!(map.num_physical_distillation_units == 2); @@ -380,7 +380,7 @@ fn test_first_round_overrides_applied() { #[test] fn iterate_for_all_distillation_units_test() { let templates = create_templates_list_2_2_2(); - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let map = create_default_map(PhysicalQubit::qubit_maj_ns_e4(), &ftp, &templates); let mut hashmap = FxHashSet::default(); @@ -410,7 +410,7 @@ fn iterate_for_all_distillation_units_test() { #[test] fn iterate_for_all_distillation_units_0_2_1_test() { let templates = create_templates_list_0_2_1(); - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let map = create_default_map(PhysicalQubit::qubit_maj_ns_e4(), &ftp, &templates); let mut hashmap = FxHashSet::default(); let mut callback = |indexes: &[usize]| { diff --git a/resource_estimator/src/system/optimization/tfactory_exhaustive/tests.rs b/resource_estimator/src/system/optimization/tfactory_exhaustive/tests.rs index 8d70b99deb..ac2ab8b84e 100644 --- a/resource_estimator/src/system/optimization/tfactory_exhaustive/tests.rs +++ b/resource_estimator/src/system/optimization/tfactory_exhaustive/tests.rs @@ -9,7 +9,10 @@ use crate::{ use super::{ super::super::{ data, - modeling::{PhysicalQubit, Protocol, TFactoryDistillationUnitTemplate}, + modeling::{ + floquet_code, surface_code_gate_based, PhysicalQubit, Protocol, + TFactoryDistillationUnitTemplate, + }, }, find_nondominated_population, find_nondominated_tfactories, }; @@ -19,7 +22,7 @@ use std::{borrow::Cow, rc::Rc, time::Instant}; fn test_one_t_error_rate() { let start = Instant::now(); let factories = find_nondominated_tfactories( - &Protocol::default(), + &surface_code_gate_based(), &Rc::new(PhysicalQubit::default()), &TFactoryDistillationUnitTemplate::default_distillation_unit_templates(), 1e-18, @@ -41,7 +44,7 @@ fn test_one_t_error_rate() { #[test] pub fn chemistry_qubit_gate_us_e3_test() { - let tfactories = find_tfactories(&Protocol::default(), "qubit_gate_us_e3"); + let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_us_e3"); assert_eq!(tfactories.len(), 3); assert_eq!(tfactories[0].physical_qubits(), 50460); @@ -55,7 +58,7 @@ pub fn chemistry_qubit_gate_us_e3_test() { #[test] pub fn chemistry_qubit_gate_us_e4_test() { - let tfactories = find_tfactories(&Protocol::default(), "qubit_gate_us_e4"); + let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_us_e4"); assert_eq!(tfactories.len(), 3); assert_eq!(tfactories[0].physical_qubits(), 13500); @@ -69,7 +72,7 @@ pub fn chemistry_qubit_gate_us_e4_test() { #[test] pub fn chemistry_qubit_gate_ns_e3_test() { - let tfactories = find_tfactories(&Protocol::default(), "qubit_gate_ns_e3"); + let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_ns_e3"); assert_eq!(tfactories.len(), 4); assert_eq!(tfactories[0].physical_qubits(), 82620); @@ -85,7 +88,7 @@ pub fn chemistry_qubit_gate_ns_e3_test() { #[test] pub fn chemistry_qubit_gate_ns_e4_test() { - let tfactories = find_tfactories(&Protocol::default(), "qubit_gate_ns_e4"); + let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_ns_e4"); assert_eq!(tfactories.len(), 3); assert_eq!(tfactories[0].physical_qubits(), 24000); @@ -99,7 +102,7 @@ pub fn chemistry_qubit_gate_ns_e4_test() { #[test] pub fn chemistry_qubit_maj_ns_e4_test() { - let tfactories = find_tfactories(&Protocol::floquet_code(), "qubit_maj_ns_e4"); + let tfactories = find_tfactories(&floquet_code(), "qubit_maj_ns_e4"); assert_eq!(tfactories.len(), 5); assert_eq!(tfactories[0].physical_qubits(), 619_814); @@ -117,7 +120,7 @@ pub fn chemistry_qubit_maj_ns_e4_test() { #[test] pub fn chemistry_qubit_maj_ns_e6_test() { - let tfactories = find_tfactories(&Protocol::floquet_code(), "qubit_maj_ns_e6"); + let tfactories = find_tfactories(&floquet_code(), "qubit_maj_ns_e6"); assert_eq!(tfactories.len(), 3); assert_eq!(tfactories[0].physical_qubits(), 24960); @@ -131,7 +134,7 @@ pub fn chemistry_qubit_maj_ns_e6_test() { #[test] fn required_logical_tstate_error_too_high() { - let ftp = Protocol::default(); + let ftp = surface_code_gate_based(); let qubit: Rc = Rc::new(PhysicalQubit::default()); let distillation_unit_templates = TFactoryDistillationUnitTemplate::default_distillation_unit_templates(); @@ -166,7 +169,7 @@ fn find_tfactories<'a>(ftp: &Protocol, qubit_name: &str) -> Vec Result<()> { - let ftp = Protocol::default(); + let ftp = surface_code_gate_based(); let qubit = Rc::new(PhysicalQubit::default()); let partitioning = ErrorBudget::new(0.5e-4, 0.5e-4, 0.0); @@ -174,7 +177,7 @@ pub fn single_tstate() -> Result<()> { #[test] pub fn perfect_tstate() -> Result<()> { - let ftp = Protocol::default(); + let ftp = surface_code_gate_based(); let qubit = Rc::new(PhysicalQubit::GateBased(GateBasedPhysicalQubit { t_gate_error_rate: 0.5e-4, ..GateBasedPhysicalQubit::default() @@ -228,7 +231,7 @@ fn validate_result_invariants( #[allow(clippy::too_many_lines)] #[test] pub fn test_hubbard_e2e() -> Result<()> { - let ftp = Protocol::default(); + let ftp = surface_code_gate_based(); let qubit = Rc::new(PhysicalQubit::default()); let (layout_overhead, partitioning) = hubbard_overhead_and_partitioning()?; let estimation = PhysicalResourceEstimation::new( @@ -271,7 +274,7 @@ pub fn test_hubbard_e2e() -> Result<()> { validate_result_invariants(&result); - let same_ftp = Protocol::default(); + let same_ftp = surface_code_gate_based(); let output_t_error_rate = part.required_output_error_rate(); let builder = create_factory_builder(); let tfactories = builder @@ -280,7 +283,9 @@ pub fn test_hubbard_e2e() -> Result<()> { &qubit, 0, output_t_error_rate, - &same_ftp.max_code_distance(), + same_ftp + .max_code_distance() + .expect("code has max code distance"), ) .expect("can compute factories"); @@ -323,7 +328,7 @@ pub fn test_hubbard_e2e() -> Result<()> { #[allow(clippy::too_many_lines)] #[test] pub fn test_hubbard_e2e_measurement_based() -> Result<()> { - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let qubit = Rc::new(PhysicalQubit::qubit_maj_ns_e6()); let (layout_overhead, partitioning) = hubbard_overhead_and_partitioning()?; let estimation = PhysicalResourceEstimation::new( @@ -365,7 +370,7 @@ pub fn test_hubbard_e2e_measurement_based() -> Result<()> { validate_result_invariants(&result); let output_t_error_rate = part.required_output_error_rate(); - let same_ftp = Protocol::floquet_code(); + let same_ftp = floquet_code(); let builder = create_factory_builder(); let tfactories = builder .find_factories( @@ -373,7 +378,9 @@ pub fn test_hubbard_e2e_measurement_based() -> Result<()> { &qubit, 0, output_t_error_rate, - &same_ftp.max_code_distance(), + same_ftp + .max_code_distance() + .expect("code has max code distance"), ) .expect("can compute factories"); @@ -415,7 +422,7 @@ pub fn test_hubbard_e2e_measurement_based() -> Result<()> { #[test] pub fn test_hubbard_e2e_increasing_max_duration() -> Result<()> { - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let qubit = Rc::new(PhysicalQubit::qubit_maj_ns_e6()); let (layout_overhead, partitioning) = hubbard_overhead_and_partitioning()?; let estimation = PhysicalResourceEstimation::new( @@ -443,7 +450,7 @@ pub fn test_hubbard_e2e_increasing_max_duration() -> Result<()> { #[test] pub fn test_hubbard_e2e_increasing_max_num_qubits() -> Result<()> { - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let qubit = Rc::new(PhysicalQubit::qubit_maj_ns_e6()); let (layout_overhead, partitioning) = hubbard_overhead_and_partitioning()?; let estimation = PhysicalResourceEstimation::new( @@ -471,7 +478,7 @@ pub fn test_hubbard_e2e_increasing_max_num_qubits() -> Result<()> { fn prepare_chemistry_estimation_with_expected_majorana( ) -> PhysicalResourceEstimation { - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let qubit = Rc::new(PhysicalQubit::qubit_maj_ns_e6()); let value = r#"{ @@ -640,7 +647,7 @@ pub fn test_chemistry_based_max_num_qubits() -> Result<()> { fn prepare_factorization_estimation_with_optimistic_majorana( ) -> PhysicalResourceEstimation { - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let qubit = Rc::new(PhysicalQubit::qubit_maj_ns_e6()); let value = r#"{ @@ -773,7 +780,7 @@ pub fn test_factorization_2048_max_num_qubits_matches_regular_estimate() -> Resu fn prepare_ising20x20_estimation_with_pessimistic_gate_based( ) -> PhysicalResourceEstimation { - let ftp = Protocol::surface_code_gate_based(); + let ftp = surface_code_gate_based(); let qubit = Rc::new(PhysicalQubit::qubit_gate_us_e3()); let value = r#"{ @@ -895,7 +902,7 @@ fn build_frontier_test() { fn prepare_bit_flip_code_resources_and_majorana_n6_qubit( ) -> PhysicalResourceEstimation { let qubit = Rc::new(PhysicalQubit::qubit_maj_ns_e6()); - let ftp = Protocol::floquet_code(); + let ftp = floquet_code(); let value = r#"{ "numQubits": 5, @@ -958,7 +965,7 @@ fn build_frontier_bit_flip_code_test() { fn code_distance_tests() { let params = JobParams::default(); - let ftp = Protocol::surface_code_gate_based(); + let ftp = surface_code_gate_based(); for logical_qubits in (50..=1000).step_by(50) { for num_cycles in (50_000..=500_000).step_by(50_000) { @@ -973,7 +980,9 @@ fn code_distance_tests() { .compute_code_parameter(&qubit, required_logical_qubit_error_rate) .expect("code distance can be computed"); - assert!(code_distance <= ftp.max_code_distance()); + assert!( + code_distance <= *ftp.max_code_distance().expect("code has max code distance") + ); } } }