Skip to content

Commit

Permalink
Control how physical qubits are computed in factories.
Browse files Browse the repository at this point in the history
  • Loading branch information
msoeken committed Sep 27, 2024
1 parent a51dcb3 commit d283643
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 19 deletions.
22 changes: 17 additions & 5 deletions resource_estimator/src/estimates/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ pub struct RoundBasedFactory<P> {
rounds: Vec<DistillationRound<P>>,
input_error_rate_before_each_round: Vec<f64>,
failure_probability_after_each_round: Vec<f64>,
separate_round_qubits: bool,
}

impl<P: Clone> RoundBasedFactory<P> {
Expand All @@ -152,20 +153,23 @@ impl<P: Clone> RoundBasedFactory<P> {
rounds: Vec<DistillationRound<P>>,
input_error_rate_before_each_round: Vec<f64>,
failure_probability_after_each_round: Vec<f64>,
separate_round_qubits: bool,
) -> Self {
Self {
length,
failure_probability_requirement,
rounds,
input_error_rate_before_each_round,
failure_probability_after_each_round,
separate_round_qubits,
}
}

pub fn build(
units: &[&impl DistillationUnit<P>],
initial_input_error_rate: f64,
failure_probability_requirement: f64,
separate_round_qubits: bool,
) -> Result<RoundBasedFactory<P>, FactoryBuildError> {
let rounds: Vec<DistillationRound<P>> = Vec::with_capacity(units.len());
let mut input_error_rate_before_each_round = Vec::with_capacity(units.len() + 1);
Expand All @@ -178,6 +182,7 @@ impl<P: Clone> RoundBasedFactory<P> {
rounds,
input_error_rate_before_each_round,
failure_probability_after_each_round,
separate_round_qubits,
};

pipeline.compute_units_per_round(units, 1)?;
Expand Down Expand Up @@ -321,11 +326,18 @@ impl<P: Clone> Factory for RoundBasedFactory<P> {
type Parameter = P;

fn physical_qubits(&self) -> u64 {
self.rounds
.iter()
.map(DistillationRound::physical_qubits)
.max()
.unwrap_or(0)
if self.separate_round_qubits {
self.rounds
.iter()
.map(DistillationRound::physical_qubits)
.sum::<u64>()
} else {
self.rounds
.iter()
.map(DistillationRound::physical_qubits)
.max()
.unwrap_or(0)
}
}

fn duration(&self) -> u64 {
Expand Down
1 change: 1 addition & 0 deletions resource_estimator/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ fn estimate_single<L: Overhead + LayoutReportData + PartitioningOverhead + Seria
TFactoryBuilder::new(
distillation_unit_templates,
job_params.constraints().max_distillation_rounds,
job_params.constraints().separate_round_qubits,
),
logical_resources.clone(),
partitioning,
Expand Down
3 changes: 3 additions & 0 deletions resource_estimator/src/system/data/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub struct Constraints {
pub max_physical_qubits: Option<u64>,
#[serde(default = "Constraints::max_distillation_rounds_default")]
pub max_distillation_rounds: u64,
#[serde(default)]
pub(crate) separate_round_qubits: bool,
}

impl Default for Constraints {
Expand All @@ -32,6 +34,7 @@ impl Default for Constraints {
max_duration: None,
max_physical_qubits: None,
max_distillation_rounds: Self::max_distillation_rounds_default(),
separate_round_qubits: false,
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion resource_estimator/src/system/modeling/tfactory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,10 @@ impl DistillationUnit<u64> for TFactoryDistillationUnit<'_> {

pub type TFactory = RoundBasedFactory<u64>;

pub fn default_t_factory(logical_qubit: &LogicalPatch<Protocol>) -> TFactory {
pub fn default_t_factory(
logical_qubit: &LogicalPatch<Protocol>,
separate_round_qubits: bool,
) -> TFactory {
let tfactory_qubit = TFactoryQubit::Logical(logical_qubit);
let template = TFactoryDistillationUnitTemplate::create_trivial_distillation_unit_1_to_1();
let unit = TFactoryDistillationUnit::by_template(&template, &tfactory_qubit);
Expand All @@ -484,6 +487,7 @@ pub fn default_t_factory(logical_qubit: &LogicalPatch<Protocol>) -> TFactory {
rounds,
input_t_error_rate_before_each_round,
failure_probability_after_each_round,
separate_round_qubits,
)
}

Expand Down
3 changes: 2 additions & 1 deletion resource_estimator/src/system/modeling/tfactory/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ fn create_pipeline_with_distance(
&unit_refs,
unit_refs[0].qubit_t_error_rate(),
failure_probability_requirement,
false,
) {
Some((
pipeline.physical_qubits(),
Expand Down Expand Up @@ -270,7 +271,7 @@ fn test_default_t_factory() {
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);
let tfactory = default_t_factory(&logical_qubit, false);

assert_eq!(tfactory.num_rounds(), 1);
assert_eq!(tfactory.num_units_per_round(), vec![1]);
Expand Down
28 changes: 24 additions & 4 deletions resource_estimator/src/system/optimization/tfactory_exhaustive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ where
/// Pareto frontier of currently best `TFactories`.
/// We optimize them by two duration and normalized qubits.
frontier_factories: Population<P>,
/// Controls how the number of physical qubits for a factory are computed:
/// if true, each round has its own physical qubits, if false, physical
/// qubits can be shared among rounds.
separate_round_qubits: bool,
}

trait TFactoryExhaustiveSearchOptions {
Expand Down Expand Up @@ -69,13 +73,14 @@ where
P: From<TFactory>,
P: TFactoryExhaustiveSearchOptions,
{
fn new(output_t_error_rate: f64) -> Self {
fn new(output_t_error_rate: f64, separate_round_qubits: bool) -> Self {
Self {
output_t_error_rate,
frontier_factories: Population::<P>::new(),
num_combinations: 0,
num_valid: 0,
num_candidates: 0,
separate_round_qubits,
}
}

Expand All @@ -95,7 +100,12 @@ where
// This is the success probability of producing the expected number of T
// states in sufficient quality (see Appendix C in paper)
#[allow(clippy::match_same_arms)]
match TFactory::build(units, units[0].qubit_t_error_rate(), 0.01) {
match TFactory::build(
units,
units[0].qubit_t_error_rate(),
0.01,
self.separate_round_qubits,
) {
Ok(factory) => {
self.num_valid += 1;
// This is the success probability of producing the expected number of T
Expand Down Expand Up @@ -217,6 +227,7 @@ pub(crate) fn find_nondominated_tfactories<'a>(
output_t_error_rate: f64,
max_code_distance: u64,
max_distillation_rounds: u64,
separate_round_qubits: bool,
) -> Vec<Cow<'a, TFactory>> {
let points = find_nondominated_population::<Point2D<TFactory>>(
ftp,
Expand All @@ -225,6 +236,7 @@ pub(crate) fn find_nondominated_tfactories<'a>(
output_t_error_rate,
max_code_distance,
max_distillation_rounds,
separate_round_qubits,
);

points
Expand All @@ -241,6 +253,7 @@ fn find_nondominated_population<P>(
output_t_error_rate: f64,
max_code_distance: u64,
max_distillation_rounds: u64,
separate_round_qubits: bool,
) -> Population<P>
where
P: Point + Ord + ToString + From<TFactory> + TFactoryExhaustiveSearchOptions,
Expand All @@ -252,7 +265,7 @@ where
let mut population = Population::<P>::new();

if let Ok(logical_qubit) = LogicalPatch::new(ftp, max_code_distance, qubit.clone()) {
let factory = default_t_factory(&logical_qubit);
let factory = default_t_factory(&logical_qubit, separate_round_qubits);
let point = P::from(factory);
population.push(point);
}
Expand All @@ -276,7 +289,8 @@ where
distillation_unit_templates,
);

let mut searcher = TFactoryExhaustiveSearch::<P>::new(output_t_error_rate);
let mut searcher =
TFactoryExhaustiveSearch::<P>::new(output_t_error_rate, separate_round_qubits);

for num_rounds in 1..=max_distillation_rounds {
process_for_num_rounds(&mut searcher, &distillation_units_map, num_rounds as usize);
Expand Down Expand Up @@ -346,17 +360,20 @@ fn process_for_specifications_combination<P>(
pub struct TFactoryBuilder {
distillation_unit_templates: Vec<TFactoryDistillationUnitTemplate>,
max_distillation_rounds: u64,
separate_round_qubits: bool,
}

impl TFactoryBuilder {
#[must_use]
pub fn new(
distillation_unit_templates: Vec<TFactoryDistillationUnitTemplate>,
max_distillation_rounds: u64,
separate_round_qubits: bool,
) -> Self {
Self {
distillation_unit_templates,
max_distillation_rounds,
separate_round_qubits,
}
}
}
Expand All @@ -366,10 +383,12 @@ impl Default for TFactoryBuilder {
let distillation_unit_templates =
TFactoryDistillationUnitTemplate::default_distillation_unit_templates();
let max_distillation_rounds = MAX_DISTILLATION_ROUNDS;
let separate_round_qubits = false;

Self {
distillation_unit_templates,
max_distillation_rounds,
separate_round_qubits,
}
}
}
Expand All @@ -392,6 +411,7 @@ impl FactoryBuilder<Protocol> for TFactoryBuilder {
output_t_error_rate,
*max_code_distance,
self.max_distillation_rounds,
self.separate_round_qubits,
))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ fn test_one_t_error_rate() {
1e-18,
35,
MAX_DISTILLATION_ROUNDS,
false,
);
let elapsed = start.elapsed();

Expand All @@ -44,7 +45,7 @@ fn test_one_t_error_rate() {

#[test]
pub fn chemistry_qubit_gate_us_e3_test() {
let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_us_e3");
let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_us_e3", false);

assert_eq!(tfactories.len(), 3);
assert_eq!(tfactories[0].physical_qubits(), 50460);
Expand All @@ -58,7 +59,7 @@ pub fn chemistry_qubit_gate_us_e3_test() {

#[test]
pub fn chemistry_qubit_gate_us_e4_test() {
let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_us_e4");
let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_us_e4", false);

assert_eq!(tfactories.len(), 3);
assert_eq!(tfactories[0].physical_qubits(), 13500);
Expand All @@ -72,7 +73,7 @@ pub fn chemistry_qubit_gate_us_e4_test() {

#[test]
pub fn chemistry_qubit_gate_ns_e3_test() {
let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_ns_e3");
let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_ns_e3", false);

assert_eq!(tfactories.len(), 4);
assert_eq!(tfactories[0].physical_qubits(), 82620);
Expand All @@ -84,11 +85,28 @@ pub fn chemistry_qubit_gate_ns_e3_test() {
assert_eq!(tfactories[1].duration(), 112_800);
assert_eq!(tfactories[2].duration(), 152_400);
assert_eq!(tfactories[3].duration(), 222_000);

let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_ns_e3", true);

assert_eq!(tfactories.len(), 6);
assert_eq!(tfactories[0].physical_qubits(), 133_080);
assert_eq!(tfactories[1].physical_qubits(), 105_540);
assert_eq!(tfactories[2].physical_qubits(), 100_032);
assert_eq!(tfactories[3].physical_qubits(), 88_720);
assert_eq!(tfactories[4].physical_qubits(), 83_212);
assert_eq!(tfactories[5].physical_qubits(), 79_848);

assert_eq!(tfactories[0].duration(), 91_200);
assert_eq!(tfactories[1].duration(), 112_800);
assert_eq!(tfactories[2].duration(), 152_400);
assert_eq!(tfactories[3].duration(), 182_400);
assert_eq!(tfactories[4].duration(), 222_000);
assert_eq!(tfactories[5].duration(), 349_600);
}

#[test]
pub fn chemistry_qubit_gate_ns_e4_test() {
let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_ns_e4");
let tfactories = find_tfactories(&surface_code_gate_based(), "qubit_gate_ns_e4", false);

assert_eq!(tfactories.len(), 3);
assert_eq!(tfactories[0].physical_qubits(), 24000);
Expand All @@ -102,7 +120,7 @@ pub fn chemistry_qubit_gate_ns_e4_test() {

#[test]
pub fn chemistry_qubit_maj_ns_e4_test() {
let tfactories = find_tfactories(&floquet_code(), "qubit_maj_ns_e4");
let tfactories = find_tfactories(&floquet_code(), "qubit_maj_ns_e4", false);

assert_eq!(tfactories.len(), 5);
assert_eq!(tfactories[0].physical_qubits(), 619_814);
Expand All @@ -120,7 +138,7 @@ pub fn chemistry_qubit_maj_ns_e4_test() {

#[test]
pub fn chemistry_qubit_maj_ns_e6_test() {
let tfactories = find_tfactories(&floquet_code(), "qubit_maj_ns_e6");
let tfactories = find_tfactories(&floquet_code(), "qubit_maj_ns_e6", false);

assert_eq!(tfactories.len(), 3);
assert_eq!(tfactories[0].physical_qubits(), 24960);
Expand Down Expand Up @@ -148,6 +166,7 @@ fn required_logical_tstate_error_too_high() {
output_t_error_rate,
max_code_distance,
MAX_DISTILLATION_ROUNDS,
false,
);

assert_eq!(population.items().len(), 1);
Expand All @@ -159,7 +178,11 @@ fn required_logical_tstate_error_too_high() {
assert_eq!(tfactory.unit_names(), vec!["trivial 1-to-1"]);
}

fn find_tfactories<'a>(ftp: &Protocol, qubit_name: &str) -> Vec<Cow<'a, TFactory>> {
fn find_tfactories<'a>(
ftp: &Protocol,
qubit_name: &str,
separate_round_qubits: bool,
) -> Vec<Cow<'a, TFactory>> {
let qubit: Rc<PhysicalQubit> = serde_json::from_str(&format!(r#"{{"name": "{qubit_name}"}}"#))
.expect("json should be valid");

Expand All @@ -171,6 +194,7 @@ fn find_tfactories<'a>(ftp: &Protocol, qubit_name: &str) -> Vec<Cow<'a, TFactory
output_t_error_rate,
*ftp.max_code_distance().expect("code has max code distance"),
MAX_DISTILLATION_ROUNDS,
separate_round_qubits,
)
}

Expand Down
5 changes: 4 additions & 1 deletion resource_estimator/src/system/test_report.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
"tstates": 0.0003333333333333333
},
"jobParams": {
"constraints": { "maxDistillationRounds": 3 },
"constraints": {
"maxDistillationRounds": 3,
"separateRoundQubits": false
},
"errorBudget": 0.001,
"estimateType": "singlePoint",
"qecScheme": {
Expand Down
1 change: 1 addition & 0 deletions resource_estimator/src/system/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,7 @@ fn create_factory_builder() -> TFactoryBuilder {
TFactoryBuilder::new(
TFactoryDistillationUnitTemplate::default_distillation_unit_templates(),
MAX_DISTILLATION_ROUNDS,
false,
)
}

Expand Down

0 comments on commit d283643

Please sign in to comment.