Skip to content

Commit

Permalink
Implements serialization for physical resource estimation (#1892)
Browse files Browse the repository at this point in the history
This adds serialization for the raw physical resource estimation result.
This improves exporting the result when working with resource estimator
through the extensibility API.

This does not affect the specialized resource estimation result for the
system implementation used in the RE integration into the VS Code plugin
and Python library.
  • Loading branch information
msoeken authored Aug 27, 2024
1 parent 4d6e26a commit d26b3cf
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 148 deletions.
5 changes: 5 additions & 0 deletions resource_estimator/src/estimates/logical_qubit.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use serde::Serialize;

use crate::estimates::{Error, ErrorCorrection};

use std::rc::Rc;
Expand All @@ -11,7 +13,10 @@ use std::rc::Rc;
/// assignment to the code parameters. It stores all computed information such
/// as the number of physical and logical qubits, cycle time, and logical error
/// rate.
#[derive(Serialize)]
#[serde(rename_all = "camelCase", bound = "E::Parameter: Serialize")]
pub struct LogicalPatch<E: ErrorCorrection> {
#[serde(skip)]
physical_qubit: Rc<E::Qubit>,
code_parameter: E::Parameter,
physical_qubits: u64,
Expand Down
143 changes: 1 addition & 142 deletions resource_estimator/src/estimates/physical_estimation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::{
};
use std::{borrow::Cow, cmp::Ordering, rc::Rc};

pub use result::FactoryPart;
pub use result::{FactoryPart, PhysicalResourceEstimationResult};

/// Trait to model quantum error correction.
///
Expand Down Expand Up @@ -213,147 +213,6 @@ where
/// factories.
fn max_code_parameter(&self) -> Option<Cow<Self::Parameter>>;
}

pub struct PhysicalResourceEstimationResult<E: ErrorCorrection, F, L> {
logical_patch: LogicalPatch<E>,
num_cycles: u64,
factory_parts: Vec<Option<FactoryPart<F>>>,
required_logical_error_rate: f64,
physical_qubits_for_factories: u64,
physical_qubits_for_algorithm: u64,
physical_qubits: u64,
runtime: u64,
rqops: u64,
layout_overhead: Rc<L>,
error_budget: ErrorBudget,
}

impl<
E: ErrorCorrection<Parameter = impl Clone>,
F: Factory<Parameter = E::Parameter> + Clone,
L: Overhead,
> PhysicalResourceEstimationResult<E, F, L>
{
pub fn new(
estimation: &PhysicalResourceEstimation<E, impl FactoryBuilder<E, Factory = F>, L>,
logical_patch: LogicalPatch<E>,
num_cycles: u64,
factory_parts: Vec<Option<FactoryPart<F>>>,
required_logical_error_rate: f64,
) -> Self {
let physical_qubits_for_factories = factory_parts
.iter()
.filter_map(|f| f.as_ref().map(FactoryPart::physical_qubits))
.sum();
let num_logical_patches = estimation
.layout_overhead
.logical_qubits()
.div_ceil(logical_patch.logical_qubits());
let physical_qubits_for_algorithm = num_logical_patches * logical_patch.physical_qubits();

let physical_qubits = physical_qubits_for_algorithm + physical_qubits_for_factories;

let runtime = (logical_patch.logical_cycle_time()) * num_cycles;

let rqops = (estimation.layout_overhead().logical_qubits() as f64
* logical_patch.logical_cycles_per_second())
.ceil() as u64;

Self {
logical_patch,
num_cycles,
factory_parts,
required_logical_error_rate,
physical_qubits_for_factories,
physical_qubits_for_algorithm,
physical_qubits,
runtime,
rqops,
layout_overhead: estimation.layout_overhead.clone(),
error_budget: estimation.error_budget().clone(),
}
}

pub fn without_factories(
estimation: &PhysicalResourceEstimation<E, impl FactoryBuilder<E, Factory = F>, L>,
logical_patch: LogicalPatch<E>,
num_cycles: u64,
required_logical_patch_error_rate: f64,
) -> Self {
Self::new(
estimation,
logical_patch,
num_cycles,
std::iter::repeat(())
.map(|()| None)
.take(estimation.factory_builder.num_magic_state_types())
.collect(),
required_logical_patch_error_rate,
)
}

pub fn logical_patch(&self) -> &LogicalPatch<E> {
&self.logical_patch
}

pub fn take(self) -> (LogicalPatch<E>, Vec<Option<FactoryPart<F>>>, ErrorBudget) {
(self.logical_patch, self.factory_parts, self.error_budget)
}

pub fn num_cycles(&self) -> u64 {
self.num_cycles
}

pub fn factory_parts(&self) -> &[Option<FactoryPart<F>>] {
&self.factory_parts
}

/// The required logical error rate for one logical operation on one logical
/// qubit
pub fn required_logical_error_rate(&self) -> f64 {
self.required_logical_error_rate
}

pub fn physical_qubits_for_factories(&self) -> u64 {
self.physical_qubits_for_factories
}

pub fn physical_qubits_for_algorithm(&self) -> u64 {
self.physical_qubits_for_algorithm
}

pub fn physical_qubits(&self) -> u64 {
self.physical_qubits
}

pub fn runtime(&self) -> u64 {
self.runtime
}

pub fn rqops(&self) -> u64 {
self.rqops
}

pub fn layout_overhead(&self) -> &Rc<L> {
&self.layout_overhead
}

pub fn error_budget(&self) -> &ErrorBudget {
&self.error_budget
}

pub fn algorithmic_logical_depth(&self) -> u64 {
self.layout_overhead.logical_depth(&self.error_budget)
}

/// The argument index indicates for which type of magic state (starting
/// from 0) the number is requested for.
pub fn num_magic_states(&self, index: usize) -> u64 {
self.layout_overhead
.num_magic_states(&self.error_budget, index)
}
}

pub struct PhysicalResourceEstimation<E: ErrorCorrection, Builder, L> {
// required parameters
ftp: E,
Expand Down
157 changes: 156 additions & 1 deletion resource_estimator/src/estimates/physical_estimation/result.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,164 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use super::Factory;
use std::rc::Rc;

use serde::Serialize;

use crate::estimates::{
ErrorBudget, ErrorCorrection, Factory, FactoryBuilder, LogicalPatch, Overhead,
PhysicalResourceEstimation,
};

/// Resource estimation result
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PhysicalResourceEstimationResult<E: ErrorCorrection, F, L> {
#[serde(bound = "E::Parameter: Serialize")]
logical_patch: LogicalPatch<E>,
num_cycles: u64,
#[serde(bound = "F: Serialize")]
factory_parts: Vec<Option<FactoryPart<F>>>,
required_logical_error_rate: f64,
physical_qubits_for_factories: u64,
physical_qubits_for_algorithm: u64,
physical_qubits: u64,
runtime: u64,
rqops: u64,
#[serde(skip)]
layout_overhead: Rc<L>,
error_budget: ErrorBudget,
}

impl<
E: ErrorCorrection<Parameter = impl Clone>,
F: Factory<Parameter = E::Parameter> + Clone,
L: Overhead,
> PhysicalResourceEstimationResult<E, F, L>
{
pub fn new(
estimation: &PhysicalResourceEstimation<E, impl FactoryBuilder<E, Factory = F>, L>,
logical_patch: LogicalPatch<E>,
num_cycles: u64,
factory_parts: Vec<Option<FactoryPart<F>>>,
required_logical_error_rate: f64,
) -> Self {
let physical_qubits_for_factories = factory_parts
.iter()
.filter_map(|f| f.as_ref().map(FactoryPart::physical_qubits))
.sum();
let num_logical_patches = estimation
.layout_overhead
.logical_qubits()
.div_ceil(logical_patch.logical_qubits());
let physical_qubits_for_algorithm = num_logical_patches * logical_patch.physical_qubits();

let physical_qubits = physical_qubits_for_algorithm + physical_qubits_for_factories;

let runtime = (logical_patch.logical_cycle_time()) * num_cycles;

let rqops = (estimation.layout_overhead().logical_qubits() as f64
* logical_patch.logical_cycles_per_second())
.ceil() as u64;

Self {
logical_patch,
num_cycles,
factory_parts,
required_logical_error_rate,
physical_qubits_for_factories,
physical_qubits_for_algorithm,
physical_qubits,
runtime,
rqops,
layout_overhead: estimation.layout_overhead.clone(),
error_budget: estimation.error_budget().clone(),
}
}

pub fn without_factories(
estimation: &PhysicalResourceEstimation<E, impl FactoryBuilder<E, Factory = F>, L>,
logical_patch: LogicalPatch<E>,
num_cycles: u64,
required_logical_patch_error_rate: f64,
) -> Self {
Self::new(
estimation,
logical_patch,
num_cycles,
std::iter::repeat(())
.map(|()| None)
.take(estimation.factory_builder.num_magic_state_types())
.collect(),
required_logical_patch_error_rate,
)
}

pub fn logical_patch(&self) -> &LogicalPatch<E> {
&self.logical_patch
}

pub fn take(self) -> (LogicalPatch<E>, Vec<Option<FactoryPart<F>>>, ErrorBudget) {
(self.logical_patch, self.factory_parts, self.error_budget)
}

pub fn num_cycles(&self) -> u64 {
self.num_cycles
}

pub fn factory_parts(&self) -> &[Option<FactoryPart<F>>] {
&self.factory_parts
}

/// The required logical error rate for one logical operation on one logical
/// qubit
pub fn required_logical_error_rate(&self) -> f64 {
self.required_logical_error_rate
}

pub fn physical_qubits_for_factories(&self) -> u64 {
self.physical_qubits_for_factories
}

pub fn physical_qubits_for_algorithm(&self) -> u64 {
self.physical_qubits_for_algorithm
}

pub fn physical_qubits(&self) -> u64 {
self.physical_qubits
}

pub fn runtime(&self) -> u64 {
self.runtime
}

pub fn rqops(&self) -> u64 {
self.rqops
}

pub fn layout_overhead(&self) -> &Rc<L> {
&self.layout_overhead
}

pub fn error_budget(&self) -> &ErrorBudget {
&self.error_budget
}

pub fn algorithmic_logical_depth(&self) -> u64 {
self.layout_overhead.logical_depth(&self.error_budget)
}

/// The argument index indicates for which type of magic state (starting
/// from 0) the number is requested for.
pub fn num_magic_states(&self, index: usize) -> u64 {
self.layout_overhead
.num_magic_states(&self.error_budget, index)
}
}

/// Results for a factory part in the overall quantum algorithm
#[derive(Serialize)]
#[serde(rename_all = "camelCase", bound = "F: Serialize")]
pub struct FactoryPart<F> {
/// The factory used in this part
factory: F,
Expand Down
Loading

0 comments on commit d26b3cf

Please sign in to comment.