Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create data classes used by the Azure physical costing model #356

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions qualtran/surface_code/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from qualtran.surface_code.algorithm_specs import AlgorithmSpecs
from qualtran.surface_code.physical_parameters import PhysicalParameters
from qualtran.surface_code.rotation_cost_model import RotationCostLinearModel, RotationCostModel
from qualtran.surface_code.t_factory import TFactory
90 changes: 90 additions & 0 deletions qualtran/surface_code/quantum_error_correction_scheme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import attr
import numpy as np
from attrs import frozen

from qualtran.surface_code.physical_parameters import PhysicalParameters


@frozen
class QuantumErrorCorrectionScheme:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this abstract?

"""QuantumErrorCorrectionScheme represents a quantum error correction scheme.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is an unhelpful line.

A full quantum error correction scheme would include a particular code, decoder, way of doing gates, and many more things. As I understand it: this class captures a small slice of the relevant characteristics of an error correcting code. Consider a more helpful name for this class. And/or consider making this abstract and giving it a concise name (and meaningful names to the implementations).


QuantumErrorCorrectionScheme provides estimates for the logical error rate,
number of physical qubits and the logical time step given a code distance and
physical assumptions.
Comment on lines +26 to +28
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't indent the body of the docstring


Attributes:
error_rate_scaler: Logical error rate coefficient.
error_rate_threshold: Logical error rate threshold.
Comment on lines +31 to +32
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document these constants in terms of the mathematical formula in which they appear. I don't think "error_rate_scaler" is a common term and "error_rate_threshold" would imply that this is a property of the code whereas in reality this is yet another fuzzy parameter that captures the empirical behavior of the error suppression.

Note that we measure/target/think about the parameter $\Lambda$ which is $p*/p$ instead of factoring things into the "physical error rate" and the "error rate threshold". Lambda is an experimentally measurable and relevant property.

Copy link
Contributor Author

@NoureldinYosri NoureldinYosri Oct 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is $\Lambda$ equal to $\frac{p*}{p}$ or to $\log{p^*}$?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition of lambda definitely includes the physical error rate $p$, so it can't just be $\log p^*$.

you can use https://arxiv.org/abs/2207.06431 as a reference for lambda. It's the error at d divided by the error at d+2; i.e. how much suppression you get by increasing the code distance.

reference: source of the estimates.
"""

error_rate_scaler = attr.ib(type=float, default=0.1, repr=lambda x: f'{x:g}')
error_rate_threshold = attr.ib(type=float, default=0.01, repr=lambda x: f'{x:g}')
reference = attr.ib(type=str, default='')

def logical_error_rate(
self, code_distance: int | np.ndarray, physical_error_rate: float | np.ndarray
) -> float | np.ndarray:
"""Computes the logical error rate."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to say something in addition to the method name

"""The logical error rate for a given code distance and physical error rate using this scheme"""

return self.error_rate_scaler * np.power(
physical_error_rate / self.error_rate_threshold, (code_distance + 1) / 2
)

def physical_qubits(self, code_distance: int | np.ndarray) -> int | np.ndarray:
"""Computes number of physical qubits"""
Comment on lines +48 to +49
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"""The number of physical qubits for the given code distance using this scheme.

also: this needs an abc.abstractmethod or NotImplementedError.


def logical_time_step(
self, code_distance: int | np.ndarray, physical_parameters: PhysicalParameters
) -> float:
"""Computes the logical time step."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what



class GateBasedSurfaceCode(QuantumErrorCorrectionScheme):
"""Gate Based Surface Code."""

def physical_qubits(self, code_distance: int | np.ndarray) -> int | np.ndarray:
return 2 * code_distance**2

def logical_time_step(
self, code_distance: int | np.ndarray, physical_parameters: PhysicalParameters
) -> float:
return (4 * physical_parameters.t_gate + 2 * physical_parameters.t_meas) * code_distance


class MeasurementBasedSurfaceCode(QuantumErrorCorrectionScheme):
"""Measurement Based Surface Code."""

def physical_qubits(self, code_distance: int | np.ndarray) -> int | np.ndarray:
return 2 * code_distance**2

def logical_time_step(
self, code_distance: int | np.ndarray, physical_parameters: PhysicalParameters
) -> float:
return 20 * physical_parameters.t_meas * code_distance


class MeasurementBasedHastingsHaahCode(QuantumErrorCorrectionScheme):
"""Measurement Based Hastings&Haah Code."""

def physical_qubits(self, code_distance: int | np.ndarray) -> int | np.ndarray:
return 4 * code_distance**2 + 8 * (code_distance - 1)

def logical_time_step(
self, code_distance: int | np.ndarray, physical_parameters: PhysicalParameters
) -> float:
return 3 * physical_parameters.t_meas * code_distance
77 changes: 77 additions & 0 deletions qualtran/surface_code/quantum_error_correction_scheme_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pytest

from qualtran.surface_code import quantum_error_correction_scheme as qecs
from qualtran.surface_code.physical_parameters import PhysicalParameters


@pytest.mark.parametrize(
'qec,want',
[
[qecs.GateBasedSurfaceCode(error_rate_scaler=0.03, error_rate_threshold=0.01), 3e-7],
[
qecs.MeasurementBasedSurfaceCode(error_rate_scaler=0.04, error_rate_threshold=0.09),
6.77e-12,
],
[
qecs.MeasurementBasedHastingsHaahCode(
error_rate_scaler=0.05, error_rate_threshold=0.06
),
6.43e-11,
],
],
)
def test_logical_error_rate(qec: qecs.QuantumErrorCorrectionScheme, want: float):
assert qec.logical_error_rate(9, 1e-3) == pytest.approx(want)


@pytest.mark.parametrize(
'qec,want',
[
[qecs.GateBasedSurfaceCode(error_rate_scaler=0.03, error_rate_threshold=0.01), 242],
[qecs.MeasurementBasedSurfaceCode(error_rate_scaler=0.04, error_rate_threshold=0.09), 242],
[
qecs.MeasurementBasedHastingsHaahCode(
error_rate_scaler=0.05, error_rate_threshold=0.06
),
564,
],
],
)
def test_physical_qubits(qec: qecs.QuantumErrorCorrectionScheme, want: int):
assert qec.physical_qubits(11) == want


@pytest.mark.parametrize(
'qec,want',
[
[qecs.GateBasedSurfaceCode(error_rate_scaler=0.03, error_rate_threshold=0.01), 4.8e-6],
[
qecs.MeasurementBasedSurfaceCode(error_rate_scaler=0.04, error_rate_threshold=0.09),
2.4e-5,
],
[
qecs.MeasurementBasedHastingsHaahCode(
error_rate_scaler=0.05, error_rate_threshold=0.06
),
3.6e-6,
],
],
)
def test_logical_time_step(qec: qecs.QuantumErrorCorrectionScheme, want: float):
assert qec.logical_time_step(
12, physical_parameters=PhysicalParameters(50e-9, 100e-9, 1e-4)
) == pytest.approx(want)
82 changes: 82 additions & 0 deletions qualtran/surface_code/rotation_cost_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import abc

import attr
import numpy as np
from attrs import frozen

_PRETTY_FLOAT = attr.ib(type=float, repr=lambda x: f'{x:g}')


class RotationCostModel(abc.ABC):
"""Analytical estimate of number of T gates needed to approximate a rotation given an error budget."""

@abc.abstractmethod
def mean_cost(self, error_budge: float | np.ndarray) -> float:
"""Returns the mean number of T gates needed to approx a rotation."""

@abc.abstractmethod
def max_cost(self, error_budge: float | np.ndarray) -> float:
"""Returns the max number of T gates needed to approx a rotation."""


@frozen
class RotationCostLinearModel(RotationCostModel):
r"""RotationCostLinearModel is a linear model in the log of the error budget.

#T gates = $-A \log_2{budget} + B$

Attributes:
A_mean: Mean value of the coefficient of $log_2{budget}$.
B_mean: Mean value of the offset/overhead.
A_max: Max value of the coefficient of $log_2{budget}$.
B_max: Max value of the offset/overhead.
"""
A_mean = _PRETTY_FLOAT
B_mean = _PRETTY_FLOAT
A_max = _PRETTY_FLOAT
B_max = _PRETTY_FLOAT

gateset = attr.ib(type=str, default='')
approximation_protocol = attr.ib(type=str, default='')
reference = attr.ib(type=str, default='')

def mean_cost(self, error_budge: float | np.ndarray) -> float:
return self.A_mean * np.log2(1.0 / error_budge) + self.B_mean

def max_cost(self, error_budge: float | np.ndarray) -> float:
return self.A_max * np.log2(1.0 / error_budge) + self.B_max


MixedFallBackCliffordT = RotationCostLinearModel(
A_mean=0.53,
B_mean=4.86,
A_max=0.57,
B_max=8.83,
gateset='Clifford+T',
approximation_protocol='Mixed fallback',
reference='https://arxiv.org/abs/2203.10064:Table1',
)

BeverlandEtAl = RotationCostLinearModel(
A_mean=0.53,
B_mean=5.3,
A_max=0.53,
B_max=5.3,
gateset='Clifford+T',
approximation_protocol='Mixed fallback',
reference='https://arxiv.org/abs/2211.07629:D2',
)
21 changes: 21 additions & 0 deletions qualtran/surface_code/rotation_cost_model_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from qualtran.surface_code.rotation_cost_model import RotationCostLinearModel


def test_linear_model():
model = RotationCostLinearModel(1, 2, 3, 4)
assert model.mean_cost(0.5) == 3
assert model.max_cost(0.5) == 7
47 changes: 47 additions & 0 deletions qualtran/surface_code/t_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import attr
from attrs import frozen

from qualtran.surface_code.magic_state_factory import MagicStateCount, MagicStateFactory

_PRETTY_FLOAT = attr.ib(type=float, default=0.0, repr=lambda x: f'{x:g}')


@frozen
class TFactory(MagicStateFactory):
"""TFactory represents a magic state factory for T states.

Attributes:
num_qubits: Number of qubits used by the factory.
duration: Time taken by the factory to produce T states.
t_states_rate: Number of T states per production cycle.
reference: Source of these estimates.
"""

num_qubits = attr.ib(type=int, default=0)
duration = _PRETTY_FLOAT
t_states_rate = _PRETTY_FLOAT
error_rate = attr.ib(type=float, default=1e-9, repr=lambda x: f'{x:g}')
reference = attr.ib(type=str, default='')

def footprint(self) -> int:
return self.num_qubits

def n_cycles(self, n_magic: MagicStateCount) -> int:
return n_magic.all_t_count() / self.t_states_rate * self.duration

def distillation_error(self, n_magic: MagicStateCount, phys_err: float) -> float:
return n_magic.all_t_count() / self.t_states_rate * phys_err
24 changes: 24 additions & 0 deletions qualtran/surface_code/t_factory_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from qualtran.surface_code.t_factory import MagicStateCount, TFactory


def test_footprint():
factory = TFactory(num_qubits=5, duration=1, t_states_rate=0.1)
magic_count = MagicStateCount(t_count=1, ccz_count=1)
assert factory.footprint() == 5
assert factory.n_cycles(magic_count) == 50
assert factory.distillation_error(magic_count, 0.5) == 25
Loading