Skip to content

Commit

Permalink
[surface code] Azure model and new AlgorithmSummary (#1157)
Browse files Browse the repository at this point in the history
* Azure model and new AlgorithmSummary

* Rotation notes
  • Loading branch information
mpharrigan authored Jul 24, 2024
1 parent 0b6c2ee commit 149fe23
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 402 deletions.
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

0 comments on commit 149fe23

Please sign in to comment.