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

[surface code] Azure model and new AlgorithmSummary #1157

Merged
merged 4 commits into from
Jul 24, 2024
Merged
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
25 changes: 25 additions & 0 deletions qualtran/resource_counting/_bloq_counts.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,31 @@ def total_t_and_ccz_count(self, ts_per_rotation: int = 11) -> Dict[str, int]:
n_t = self.t + ts_per_rotation * self.rotation
return {'n_t': n_t, 'n_ccz': n_ccz}

def total_beverland_count(self) -> Dict[str, int]:
r"""Counts used by Beverland. et. al. using notation from the reference.

- $M_\mathrm{meas}$ is the number of measurements.
- $M_R$ is the number of rotations.
- $M_T$ is the number of T operations.
- $3*M_mathrm{Tof}$ is the number of Toffoli operations.
- $D_R$ is the number of layers containing at least one rotation. This can be smaller than
the total number of non-Clifford layers since it excludes layers consisting only of T or
Toffoli gates. Since we don't compile the 'layers' explicitly, we set this to be the
number of rotations.

Reference:
https://arxiv.org/abs/2211.07629.
Equation D3.
"""
toffoli = self.toffoli + self.and_bloq + self.cswap
return {
'meas': self.measurement,
'R': self.rotation,
'T': self.t,
'Tof': toffoli,
'D_R': self.rotation,
}


@frozen
class QECGatesCost(CostKey[GateCounts]):
Expand Down
109 changes: 13 additions & 96 deletions qualtran/surface_code/algorithm_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,118 +14,35 @@

from typing import Optional, TYPE_CHECKING

from attrs import field, frozen
from attrs import frozen

from qualtran.resource_counting import get_cost_value, QECGatesCost, QubitCount
from qualtran.surface_code.magic_count import MagicCount
from qualtran.surface_code.rotation_cost_model import RotationCostModel
from qualtran.resource_counting import GateCounts, get_cost_value, QECGatesCost, QubitCount

if TYPE_CHECKING:
from qualtran import Bloq

_PRETTY_FLOAT = field(default=0.0, converter=float, repr=lambda x: f'{x:g}')

_QUBIT_COUNT = QubitCount()
_QEC_COUNT = QECGatesCost()


@frozen
@frozen(kw_only=True)
class AlgorithmSummary:
"""Properties of a quantum algorithm that impact its physical cost
"""Logical costs of a quantum algorithm that impact modeling of its physical cost.

Counts of different properties that affect the physical cost of
running an algorithm (e.g. number of T gates).
All counts default to zero.

Attributes:
algorithm_qubits: Number of qubits used by the algorithm $Q_{alg}$.
measurements: Number of Measurements $M_R$.
t_gates: Number of T gates $M_T$.
toffoli_gates: Number of Toffoli gates $M_{Tof}$.
rotation_gates: Number of Rotations $M_R$.
rotation_circuit_depth: Depth of rotation circuit $D_R$.
n_rotation_layers: In Qualtran, we don't actually push all the cliffords out and count
the number of rotation layers, so this is just the number of rotations $M_R$ by default.
If you are trying to reproduce numbers exactly, you can provide an explicit
number of rotation layers.
"""

algorithm_qubits: float = _PRETTY_FLOAT
measurements: float = _PRETTY_FLOAT
t_gates: float = _PRETTY_FLOAT
toffoli_gates: float = _PRETTY_FLOAT
rotation_gates: float = _PRETTY_FLOAT
rotation_circuit_depth: float = _PRETTY_FLOAT

def __mul__(self, other: int) -> 'AlgorithmSummary':
if not isinstance(other, int):
raise TypeError(
f"Multiplication isn't supported between AlgorithmSummary and non integer type {type(other)}"
)

return AlgorithmSummary(
algorithm_qubits=self.algorithm_qubits * other,
measurements=self.measurements * other,
t_gates=self.t_gates * other,
toffoli_gates=self.toffoli_gates * other,
rotation_gates=self.rotation_gates * other,
rotation_circuit_depth=self.rotation_circuit_depth * other,
)

def __rmul__(self, other: int) -> 'AlgorithmSummary':
return self.__mul__(other)

def __add__(self, other: 'AlgorithmSummary') -> 'AlgorithmSummary':
if not isinstance(other, AlgorithmSummary):
raise TypeError(
f"Addition isn't supported between AlgorithmSummary and type {type(other)}"
)
return AlgorithmSummary(
algorithm_qubits=self.algorithm_qubits + other.algorithm_qubits,
measurements=self.measurements + other.measurements,
t_gates=self.t_gates + other.t_gates,
toffoli_gates=self.toffoli_gates + other.toffoli_gates,
rotation_gates=self.rotation_gates + other.rotation_gates,
rotation_circuit_depth=self.rotation_circuit_depth + other.rotation_circuit_depth,
)

def __sub__(self, other: 'AlgorithmSummary') -> 'AlgorithmSummary':
if not isinstance(other, AlgorithmSummary):
raise TypeError(
f"Subtraction isn't supported between AlgorithmSummary and type {type(other)}"
)
return AlgorithmSummary(
algorithm_qubits=self.algorithm_qubits - other.algorithm_qubits,
measurements=self.measurements - other.measurements,
t_gates=self.t_gates - other.t_gates,
toffoli_gates=self.toffoli_gates - other.toffoli_gates,
rotation_gates=self.rotation_gates - other.rotation_gates,
rotation_circuit_depth=self.rotation_circuit_depth - other.rotation_circuit_depth,
)

def to_magic_count(
self,
rotation_model: Optional[RotationCostModel] = None,
error_budget: Optional[float] = None,
) -> MagicCount:
ret = MagicCount(n_t=self.t_gates, n_ccz=self.toffoli_gates)
if self.rotation_gates > 0:
if rotation_model is None or error_budget is None:
raise ValueError(
'Rotation cost model and error budget must be provided to calculate rotation cost'
)
ret = (
ret
+ rotation_model.prepartion_overhead(error_budget)
+ self.rotation_gates
* rotation_model.rotation_cost(error_budget / self.rotation_gates)
)
return ret
n_algo_qubits: int
n_logical_gates: GateCounts
n_rotation_layers: Optional[int] = None

@staticmethod
def from_bloq(bloq: 'Bloq') -> 'AlgorithmSummary':
gate_count = get_cost_value(bloq, _QEC_COUNT)
return AlgorithmSummary(
t_gates=gate_count.t,
toffoli_gates=gate_count.toffoli + gate_count.and_bloq + gate_count.cswap,
rotation_gates=gate_count.rotation,
measurements=gate_count.measurement,
rotation_circuit_depth=gate_count.rotation,
algorithm_qubits=float(get_cost_value(bloq, _QUBIT_COUNT)),
)
qubit_count = int(get_cost_value(bloq, _QUBIT_COUNT))
return AlgorithmSummary(n_algo_qubits=qubit_count, n_logical_gates=gate_count)
101 changes: 18 additions & 83 deletions qualtran/surface_code/algorithm_summary_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,105 +16,40 @@
import pytest

from qualtran.bloqs import basic_gates, mcmt, rotations
from qualtran.surface_code.algorithm_summary import AlgorithmSummary
from qualtran.surface_code.magic_count import MagicCount


def test_mul():
assert AlgorithmSummary(t_gates=9) == 3 * AlgorithmSummary(t_gates=3)

with pytest.raises(TypeError):
_ = complex(1, 0) * AlgorithmSummary(rotation_gates=1) # type: ignore[operator]


def test_addition():
with pytest.raises(TypeError):
_ = AlgorithmSummary() + 5 # type: ignore[operator]

a = AlgorithmSummary(
algorithm_qubits=7,
measurements=8,
t_gates=8,
toffoli_gates=9,
rotation_gates=8,
rotation_circuit_depth=3,
)
b = AlgorithmSummary(
algorithm_qubits=4,
measurements=1,
t_gates=1,
toffoli_gates=4,
rotation_gates=2,
rotation_circuit_depth=1,
)
assert a + b == AlgorithmSummary(
algorithm_qubits=11,
measurements=9,
t_gates=9,
toffoli_gates=13,
rotation_gates=10,
rotation_circuit_depth=4,
)


def test_subtraction():
with pytest.raises(TypeError):
_ = AlgorithmSummary() - 5 # type: ignore[operator]

a = AlgorithmSummary(
algorithm_qubits=7,
measurements=8,
t_gates=8,
toffoli_gates=9,
rotation_gates=8,
rotation_circuit_depth=3,
)
b = AlgorithmSummary(
algorithm_qubits=4,
measurements=1,
t_gates=1,
toffoli_gates=4,
rotation_gates=2,
rotation_circuit_depth=1,
)
assert a - b == AlgorithmSummary(
algorithm_qubits=3,
measurements=7,
t_gates=7,
toffoli_gates=5,
rotation_gates=6,
rotation_circuit_depth=2,
)

assert AlgorithmSummary(t_gates=1, toffoli_gates=4).to_magic_count() == MagicCount(
n_ccz=4, n_t=1
)

with pytest.raises(ValueError):
_ = AlgorithmSummary(rotation_gates=1).to_magic_count()
from qualtran.resource_counting import GateCounts
from qualtran.surface_code import AlgorithmSummary


@pytest.mark.parametrize(
['bloq', 'summary'],
[
[basic_gates.TGate(is_adjoint=False), AlgorithmSummary(algorithm_qubits=1, t_gates=1)],
[basic_gates.Toffoli(), AlgorithmSummary(algorithm_qubits=3, toffoli_gates=1)],
[basic_gates.TwoBitCSwap(), AlgorithmSummary(algorithm_qubits=3, toffoli_gates=1)],
[mcmt.And(), AlgorithmSummary(algorithm_qubits=3, toffoli_gates=1)],
[
basic_gates.TGate(is_adjoint=False),
AlgorithmSummary(n_algo_qubits=1, n_logical_gates=GateCounts(t=1)),
],
[
basic_gates.Toffoli(),
AlgorithmSummary(n_algo_qubits=3, n_logical_gates=GateCounts(toffoli=1)),
],
[
basic_gates.TwoBitCSwap(),
AlgorithmSummary(n_algo_qubits=3, n_logical_gates=GateCounts(cswap=1)),
],
[mcmt.And(), AlgorithmSummary(n_algo_qubits=3, n_logical_gates=GateCounts(and_bloq=1))],
[
basic_gates.ZPowGate(exponent=0.1, global_shift=0.0, eps=1e-11),
AlgorithmSummary(algorithm_qubits=1, rotation_gates=1, rotation_circuit_depth=1),
AlgorithmSummary(n_algo_qubits=1, n_logical_gates=GateCounts(rotation=1)),
],
[
rotations.phase_gradient.PhaseGradientUnitary(
bitsize=10, exponent=1, is_controlled=False, eps=1e-10
),
AlgorithmSummary(algorithm_qubits=10, rotation_gates=10, rotation_circuit_depth=10),
AlgorithmSummary(n_algo_qubits=10, n_logical_gates=GateCounts(rotation=10)),
],
[
mcmt.MultiControlPauli(cvs=(1, 1, 1), target_gate=cirq.X),
AlgorithmSummary(
algorithm_qubits=6, toffoli_gates=2, rotation_circuit_depth=0, measurements=2
n_algo_qubits=6, n_logical_gates=GateCounts(and_bloq=2, measurement=2, clifford=3)
),
],
],
Expand Down
Loading
Loading