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") + ); } } }